summaryrefslogtreecommitdiff
path: root/MdeModulePkg
diff options
context:
space:
mode:
authorvanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524>2007-07-30 02:37:10 +0000
committervanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524>2007-07-30 02:37:10 +0000
commit772db4bb33ae66fa20e39f786b5f80d107d450a5 (patch)
tree206a2d4756e0f7e245e08ca75f3ba10df2e1cf7a /MdeModulePkg
parenteca7eaf49be5f23e3c79270621df7097ef585349 (diff)
downloadedk2-platforms-772db4bb33ae66fa20e39f786b5f80d107d450a5.tar.xz
Import ArpDxe, Dhcp4Dxe, Ip4Dxe, Mtftp4Dxe, PxeBcDxe and PxeDhcp4Dxe.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3492 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'MdeModulePkg')
-rw-r--r--MdeModulePkg/Include/Library/IpIoLib.h5
-rw-r--r--MdeModulePkg/Include/Library/NetLib.h360
-rw-r--r--MdeModulePkg/Include/Protocol/PxeDhcp4.h350
-rw-r--r--MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h85
-rw-r--r--MdeModulePkg/Include/Protocol/Tcp.h108
-rw-r--r--MdeModulePkg/Library/DxeNetLib/DxeNetLib.c1
-rw-r--r--MdeModulePkg/MdeModulePkg.dec4
-rw-r--r--MdeModulePkg/MdeModulePkg.dsc8
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h30
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c763
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h84
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf59
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa72
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c1628
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h341
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c727
-rw-r--r--MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c156
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c162
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c665
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h67
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf65
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa77
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c914
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h159
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c1631
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h115
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c906
-rw-r--r--MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h266
-rw-r--r--MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c24
-rw-r--r--MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c1
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c166
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c421
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h143
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c932
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h84
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf81
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa101
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c372
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h99
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c1146
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h238
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c629
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h120
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c2022
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h258
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c1235
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h138
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c231
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h52
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c457
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h54
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c690
-rw-r--r--MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h151
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c16
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c1
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h1
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c5
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c161
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c643
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h69
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf68
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa79
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c892
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h176
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c542
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h73
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c735
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c591
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h96
-rw-r--r--MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c522
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c2401
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h564
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c160
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h632
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.c46
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h36
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Hton.h43
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h26
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h736
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h26
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf92
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa106
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c583
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c3284
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c421
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c846
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c2193
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c517
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c1614
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h154
-rw-r--r--MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h26
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c169
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c355
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h353
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf64
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa78
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c784
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c247
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c408
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c191
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c258
-rw-r--r--MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c1086
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/snp.h14
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/start.c2
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c8
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c1
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c4
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c24
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h1
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c12
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c1
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c84
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c64
113 files changed, 42735 insertions, 302 deletions
diff --git a/MdeModulePkg/Include/Library/IpIoLib.h b/MdeModulePkg/Include/Library/IpIoLib.h
index 894e07bed4..e2c9f67559 100644
--- a/MdeModulePkg/Include/Library/IpIoLib.h
+++ b/MdeModulePkg/Include/Library/IpIoLib.h
@@ -73,11 +73,6 @@ typedef struct _ICMP_ERROR_INFO {
BOOLEAN Notify;
} ICMP_ERROR_INFO;
-//
-// Driver Consumed Protocol Prototypes
-//
-//@MT:#include EFI_PROTOCOL_CONSUMER (Ip4)
-//@MT:#include EFI_PROTOCOL_CONSUMER (Udp4)
#define EFI_IP4_HEADER_LEN(HdrPtr) ((HdrPtr)->HeaderLength << 2)
diff --git a/MdeModulePkg/Include/Library/NetLib.h b/MdeModulePkg/Include/Library/NetLib.h
index 150b97936e..fe240442ae 100644
--- a/MdeModulePkg/Include/Library/NetLib.h
+++ b/MdeModulePkg/Include/Library/NetLib.h
@@ -29,189 +29,189 @@ Abstract:
#include <Protocol/DriverConfiguration.h>
#include <Protocol/DriverDiagnostics.h>
-#define EFI_NET_LITTLE_ENDIAN
-
-typedef UINT32 IP4_ADDR;
-typedef UINT32 TCP_SEQNO;
-typedef UINT16 TCP_PORTNO;
-
-enum {
- NET_ETHER_ADDR_LEN = 6,
- NET_IFTYPE_ETHERNET = 0x01,
-
- EFI_IP_PROTO_UDP = 0x11,
- EFI_IP_PROTO_TCP = 0x06,
- EFI_IP_PROTO_ICMP = 0x01,
-
- //
- // The address classfication
- //
- IP4_ADDR_CLASSA = 1,
- IP4_ADDR_CLASSB,
- IP4_ADDR_CLASSC,
- IP4_ADDR_CLASSD,
- IP4_ADDR_CLASSE,
-
- IP4_MASK_NUM = 33,
-};
-
-#pragma pack(1)
-
-//
-// Ethernet head definition
-//
-typedef struct {
- UINT8 DstMac [NET_ETHER_ADDR_LEN];
- UINT8 SrcMac [NET_ETHER_ADDR_LEN];
- UINT16 EtherType;
-} ETHER_HEAD;
-
-
-//
-// The EFI_IP4_HEADER is hard to use because the source and
-// destination address are defined as EFI_IPv4_ADDRESS, which
-// is a structure. Two structures can't be compared or masked
-// directly. This is why there is an internal representation.
-//
-typedef struct {
-#ifdef EFI_NET_LITTLE_ENDIAN
- UINT8 HeadLen : 4;
- UINT8 Ver : 4;
-#else
- UINT8 Ver : 4;
- UINT8 HeadLen : 4;
-#endif
- UINT8 Tos;
- UINT16 TotalLen;
- UINT16 Id;
- UINT16 Fragment;
- UINT8 Ttl;
- UINT8 Protocol;
- UINT16 Checksum;
- IP4_ADDR Src;
- IP4_ADDR Dst;
-} IP4_HEAD;
-
-
-//
-// ICMP head definition. ICMP message is categoried as either an error
-// message or query message. Two message types have their own head format.
-//
-typedef struct {
- UINT8 Type;
- UINT8 Code;
- UINT16 Checksum;
-} IP4_ICMP_HEAD;
-
-typedef struct {
- IP4_ICMP_HEAD Head;
- UINT32 Fourth; // 4th filed of the head, it depends on Type.
- IP4_HEAD IpHead;
-} IP4_ICMP_ERROR_HEAD;
-
-typedef struct {
- IP4_ICMP_HEAD Head;
- UINT16 Id;
- UINT16 Seq;
-} IP4_ICMP_QUERY_HEAD;
-
-
-//
-// UDP header definition
-//
-typedef struct {
- UINT16 SrcPort;
- UINT16 DstPort;
- UINT16 Length;
- UINT16 Checksum;
-} EFI_UDP4_HEADER;
-
-
-//
-// TCP header definition
-//
-typedef struct {
- TCP_PORTNO SrcPort;
- TCP_PORTNO DstPort;
- TCP_SEQNO Seq;
- TCP_SEQNO Ack;
-#ifdef EFI_NET_LITTLE_ENDIAN
- UINT8 Res : 4;
- UINT8 HeadLen : 4;
-#else
- UINT8 HeadLen : 4;
- UINT8 Res : 4;
-#endif
- UINT8 Flag;
- UINT16 Wnd;
- UINT16 Checksum;
- UINT16 Urg;
-} TCP_HEAD;
-
-#pragma pack()
-
-#define NET_MAC_EQUAL(pMac1, pMac2, Len) \
- (NetCompareMem ((pMac1), (pMac2), Len) == 0)
-
-#define NET_MAC_IS_MULTICAST(Mac, BMac, Len) \
- (((*((UINT8 *) Mac) & 0x01) == 0x01) && (!NET_MAC_EQUAL (Mac, BMac, Len)))
-
-#ifdef EFI_NET_LITTLE_ENDIAN
-#define NTOHL(x) (UINT32)((((UINT32) (x) & 0xff) << 24) | \
- (((UINT32) (x) & 0xff00) << 8) | \
- (((UINT32) (x) & 0xff0000) >> 8) | \
- (((UINT32) (x) & 0xff000000) >> 24))
-
-#define HTONL(x) NTOHL(x)
-
-#define NTOHS(x) (UINT16)((((UINT16) (x) & 0xff) << 8) | \
- (((UINT16) (x) & 0xff00) >> 8))
-
-#define HTONS(x) NTOHS(x)
-#else
-#define NTOHL(x) (UINT32)(x)
-#define HTONL(x) (UINT32)(x)
-#define NTOHS(x) (UINT16)(x)
-#define HTONS(x) (UINT16)(x)
-#endif
-
-//
-// Test the IP's attribute, All the IPs are in host byte order.
-//
-#define IP4_IS_MULTICAST(Ip) (((Ip) & 0xF0000000) == 0xE0000000)
-#define IP4_IS_LOCAL_BROADCAST(Ip) ((Ip) == 0xFFFFFFFF)
-#define IP4_NET_EQUAL(Ip1, Ip2, NetMask) (((Ip1) & (NetMask)) == ((Ip2) & (NetMask)))
-#define IP4_IS_VALID_NETMASK(Ip) (NetGetMaskLength (Ip) != IP4_MASK_NUM)
-
-//
-// Convert the EFI_IP4_ADDRESS to plain UINT32 IP4 address.
-//
-#define EFI_IP4(EfiIpAddr) (*(IP4_ADDR *) ((EfiIpAddr).Addr))
-#define EFI_NTOHL(EfiIp) (NTOHL (EFI_IP4 ((EfiIp))))
-#define EFI_IP_EQUAL(Ip1, Ip2) (EFI_IP4 (Ip1) == EFI_IP4 (Ip2))
-
-INTN
-NetGetMaskLength (
- IN IP4_ADDR Mask
- );
-
-INTN
-NetGetIpClass (
- IN IP4_ADDR Addr
- );
-
-BOOLEAN
-Ip4IsUnicast (
- IN IP4_ADDR Ip,
- IN IP4_ADDR NetMask
- );
-
+#define EFI_NET_LITTLE_ENDIAN
+
+typedef UINT32 IP4_ADDR;
+typedef UINT32 TCP_SEQNO;
+typedef UINT16 TCP_PORTNO;
+
+enum {
+ NET_ETHER_ADDR_LEN = 6,
+ NET_IFTYPE_ETHERNET = 0x01,
+
+ EFI_IP_PROTO_UDP = 0x11,
+ EFI_IP_PROTO_TCP = 0x06,
+ EFI_IP_PROTO_ICMP = 0x01,
+
+ //
+ // The address classfication
+ //
+ IP4_ADDR_CLASSA = 1,
+ IP4_ADDR_CLASSB,
+ IP4_ADDR_CLASSC,
+ IP4_ADDR_CLASSD,
+ IP4_ADDR_CLASSE,
+
+ IP4_MASK_NUM = 33,
+};
+
+#pragma pack(1)
+
+//
+// Ethernet head definition
+//
+typedef struct {
+ UINT8 DstMac [NET_ETHER_ADDR_LEN];
+ UINT8 SrcMac [NET_ETHER_ADDR_LEN];
+ UINT16 EtherType;
+} ETHER_HEAD;
+
+
+//
+// The EFI_IP4_HEADER is hard to use because the source and
+// destination address are defined as EFI_IPv4_ADDRESS, which
+// is a structure. Two structures can't be compared or masked
+// directly. This is why there is an internal representation.
+//
+typedef struct {
+#ifdef EFI_NET_LITTLE_ENDIAN
+ UINT8 HeadLen : 4;
+ UINT8 Ver : 4;
+#else
+ UINT8 Ver : 4;
+ UINT8 HeadLen : 4;
+#endif
+ UINT8 Tos;
+ UINT16 TotalLen;
+ UINT16 Id;
+ UINT16 Fragment;
+ UINT8 Ttl;
+ UINT8 Protocol;
+ UINT16 Checksum;
+ IP4_ADDR Src;
+ IP4_ADDR Dst;
+} IP4_HEAD;
+
+
+//
+// ICMP head definition. ICMP message is categoried as either an error
+// message or query message. Two message types have their own head format.
+//
+typedef struct {
+ UINT8 Type;
+ UINT8 Code;
+ UINT16 Checksum;
+} IP4_ICMP_HEAD;
+
+typedef struct {
+ IP4_ICMP_HEAD Head;
+ UINT32 Fourth; // 4th filed of the head, it depends on Type.
+ IP4_HEAD IpHead;
+} IP4_ICMP_ERROR_HEAD;
+
+typedef struct {
+ IP4_ICMP_HEAD Head;
+ UINT16 Id;
+ UINT16 Seq;
+} IP4_ICMP_QUERY_HEAD;
+
+
+//
+// UDP header definition
+//
+typedef struct {
+ UINT16 SrcPort;
+ UINT16 DstPort;
+ UINT16 Length;
+ UINT16 Checksum;
+} EFI_UDP4_HEADER;
+
+
+//
+// TCP header definition
+//
+typedef struct {
+ TCP_PORTNO SrcPort;
+ TCP_PORTNO DstPort;
+ TCP_SEQNO Seq;
+ TCP_SEQNO Ack;
+#ifdef EFI_NET_LITTLE_ENDIAN
+ UINT8 Res : 4;
+ UINT8 HeadLen : 4;
+#else
+ UINT8 HeadLen : 4;
+ UINT8 Res : 4;
+#endif
+ UINT8 Flag;
+ UINT16 Wnd;
+ UINT16 Checksum;
+ UINT16 Urg;
+} TCP_HEAD;
+
+#pragma pack()
+
+#define NET_MAC_EQUAL(pMac1, pMac2, Len) \
+ (NetCompareMem ((pMac1), (pMac2), Len) == 0)
+
+#define NET_MAC_IS_MULTICAST(Mac, BMac, Len) \
+ (((*((UINT8 *) Mac) & 0x01) == 0x01) && (!NET_MAC_EQUAL (Mac, BMac, Len)))
+
+#ifdef EFI_NET_LITTLE_ENDIAN
+#define NTOHL(x) (UINT32)((((UINT32) (x) & 0xff) << 24) | \
+ (((UINT32) (x) & 0xff00) << 8) | \
+ (((UINT32) (x) & 0xff0000) >> 8) | \
+ (((UINT32) (x) & 0xff000000) >> 24))
+
+#define HTONL(x) NTOHL(x)
+
+#define NTOHS(x) (UINT16)((((UINT16) (x) & 0xff) << 8) | \
+ (((UINT16) (x) & 0xff00) >> 8))
+
+#define HTONS(x) NTOHS(x)
+#else
+#define NTOHL(x) (UINT32)(x)
+#define HTONL(x) (UINT32)(x)
+#define NTOHS(x) (UINT16)(x)
+#define HTONS(x) (UINT16)(x)
+#endif
+
+//
+// Test the IP's attribute, All the IPs are in host byte order.
+//
+#define IP4_IS_MULTICAST(Ip) (((Ip) & 0xF0000000) == 0xE0000000)
+#define IP4_IS_LOCAL_BROADCAST(Ip) ((Ip) == 0xFFFFFFFF)
+#define IP4_NET_EQUAL(Ip1, Ip2, NetMask) (((Ip1) & (NetMask)) == ((Ip2) & (NetMask)))
+#define IP4_IS_VALID_NETMASK(Ip) (NetGetMaskLength (Ip) != IP4_MASK_NUM)
+
+//
+// Convert the EFI_IP4_ADDRESS to plain UINT32 IP4 address.
+//
+#define EFI_IP4(EfiIpAddr) (*(IP4_ADDR *) ((EfiIpAddr).Addr))
+#define EFI_NTOHL(EfiIp) (NTOHL (EFI_IP4 ((EfiIp))))
+#define EFI_IP4_EQUAL(Ip1, Ip2) (NetCompareMem (&(Ip1), &(Ip2), sizeof (EFI_IPv4_ADDRESS)) == 0)
+
+INTN
+NetGetMaskLength (
+ IN IP4_ADDR Mask
+ );
+
+INTN
+NetGetIpClass (
+ IN IP4_ADDR Addr
+ );
+
+BOOLEAN
+Ip4IsUnicast (
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR NetMask
+ );
+
extern IP4_ADDR mIp4AllMasks [IP4_MASK_NUM];
-//@MT:#include EFI_PROTOCOL_CONSUMER (LoadedImage)
-//@MT:#include EFI_PROTOCOL_CONSUMER (ServiceBinding)
-//@MT:#include EFI_PROTOCOL_CONSUMER (SimpleNetwork)
+extern EFI_IPv4_ADDRESS mZeroIp4Addr;
+
+#define NET_IS_DIGIT(Ch) (('0' <= (Ch)) && ((Ch) <= '9'))
//
// Wrap functions to ease the impact of EFI library changes.
//
diff --git a/MdeModulePkg/Include/Protocol/PxeDhcp4.h b/MdeModulePkg/Include/Protocol/PxeDhcp4.h
new file mode 100644
index 0000000000..53fe743f55
--- /dev/null
+++ b/MdeModulePkg/Include/Protocol/PxeDhcp4.h
@@ -0,0 +1,350 @@
+/*++
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4.h
+
+Abstract:
+ EFI PXE DHCPv4 protocol definition
+
+--*/
+
+#ifndef _PXEDHCP4_H_
+#define _PXEDHCP4_H_
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+//
+// PXE DHCPv4 GUID definition
+//
+
+#define EFI_PXE_DHCP4_PROTOCOL_GUID \
+ { 0x03c4e624, 0xac28, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x29, 0x3f, 0xc1, 0x4d } }
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+//
+// Interface definition
+//
+
+typedef struct _EFI_PXE_DHCP4_PROTOCOL EFI_PXE_DHCP4_PROTOCOL;
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+//
+// Descriptions of the DHCP version 4 header and options can be found
+// in RFC-2131 and RFC-2132 at www.ietf.org
+//
+
+#pragma pack(1)
+typedef struct {
+
+ UINT8 op;
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+ UINT8 htype;
+
+ UINT8 hlen;
+
+ UINT8 hops;
+
+ UINT32 xid;
+
+ UINT16 secs;
+#define DHCP4_INITIAL_SECONDS 4
+
+ UINT16 flags;
+#define DHCP4_BROADCAST_FLAG 0x8000
+
+ UINT32 ciaddr;
+
+ UINT32 yiaddr;
+
+ UINT32 siaddr;
+
+ UINT32 giaddr;
+
+ UINT8 chaddr[16];
+
+ UINT8 sname[64];
+
+ UINT8 fname[128];
+
+//
+// This is the minimum option length as specified in RFC-2131.
+// The packet must be padded out this far with DHCP4_PAD.
+// DHCPv4 packets are usually 576 bytes in length. This length
+// includes the IPv4 and UDPv4 headers but not the media header.
+// Note: Not all DHCP relay agents will forward DHCPv4 packets
+// if they are less than 384 bytes or exceed 576 bytes. Even if
+// the underlying hardware can handle smaller and larger packets,
+// many older relay agents will not accept them.
+//
+ UINT32 magik;
+#define DHCP4_MAGIK_NUMBER 0x63825363
+
+ UINT8 options[308];
+
+} DHCP4_HEADER;
+#pragma pack()
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+//
+// DHCPv4 packet definition. Room for 576 bytes including IP and
+// UDP header.
+//
+
+#define DHCP4_MAX_PACKET_SIZE 576
+#define DHCP4_UDP_HEADER_SIZE 8
+#define DHCP4_IP_HEADER_SIZE 20
+
+#pragma pack(1)
+typedef union _DHCP4_PACKET {
+ UINT32 _force_data_alignment;
+
+ UINT8 raw[1500];
+
+ DHCP4_HEADER dhcp4;
+} DHCP4_PACKET;
+#pragma pack()
+
+#define DHCP4_SERVER_PORT 67
+#define DHCP4_CLIENT_PORT 68
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+//
+// DHCPv4 and PXE option numbers.
+//
+
+#define DHCP4_PAD 0
+#define DHCP4_END 255
+#define DHCP4_SUBNET_MASK 1
+#define DHCP4_TIME_OFFSET 2
+#define DHCP4_ROUTER_LIST 3
+#define DHCP4_TIME_SERVERS 4
+#define DHCP4_NAME_SERVERS 5
+#define DHCP4_DNS_SERVERS 6
+#define DHCP4_LOG_SERVERS 7
+#define DHCP4_COOKIE_SERVERS 8
+#define DHCP4_LPR_SREVERS 9
+#define DHCP4_IMPRESS_SERVERS 10
+#define DHCP4_RESOURCE_LOCATION_SERVERS 11
+#define DHCP4_HOST_NAME 12
+#define DHCP4_BOOT_FILE_SIZE 13
+#define DHCP4_DUMP_FILE 14
+#define DHCP4_DOMAIN_NAME 15
+#define DHCP4_SWAP_SERVER 16
+#define DHCP4_ROOT_PATH 17
+#define DHCP4_EXTENSION_PATH 18
+#define DHCP4_IP_FORWARDING 19
+#define DHCP4_NON_LOCAL_SOURCE_ROUTE 20
+#define DHCP4_POLICY_FILTER 21
+#define DHCP4_MAX_DATAGRAM_SIZE 22
+#define DHCP4_DEFAULT_TTL 23
+#define DHCP4_MTU_AGING_TIMEOUT 24
+#define DHCP4_MTU_SIZES 25
+#define DHCP4_MTU_TO_USE 26
+#define DHCP4_ALL_SUBNETS_LOCAL 27
+#define DHCP4_BROADCAST_ADDRESS 28
+#define DHCP4_PERFORM_MASK_DISCOVERY 29
+#define DHCP4_RESPOND_TO_MASK_REQ 30
+#define DHCP4_PERFORM_ROUTER_DISCOVERY 31
+#define DHCP4_ROUTER_SOLICIT_ADDRESS 32
+#define DHCP4_STATIC_ROUTER_LIST 33
+#define DHCP4_USE_ARP_TRAILERS 34
+#define DHCP4_ARP_CACHE_TIMEOUT 35
+#define DHCP4_ETHERNET_ENCAPSULATION 36
+#define DHCP4_TCP_DEFAULT_TTL 37
+#define DHCP4_TCP_KEEP_ALIVE_INT 38
+#define DHCP4_KEEP_ALIVE_GARBAGE 39
+#define DHCP4_NIS_DOMAIN_NAME 40
+#define DHCP4_NIS_SERVERS 41
+#define DHCP4_NTP_SERVERS 42
+#define DHCP4_VENDOR_SPECIFIC 43
+# define PXE_MTFTP_IP 1
+# define PXE_MTFTP_CPORT 2
+# define PXE_MTFTP_SPORT 3
+# define PXE_MTFTP_TMOUT 4
+# define PXE_MTFTP_DELAY 5
+# define PXE_DISCOVERY_CONTROL 6
+# define PXE_DISABLE_BROADCAST_DISCOVERY 0x01
+# define PXE_DISABLE_MULTICAST_DISCOVERY 0x02
+# define PXE_ACCEPT_ONLY_PXE_BOOT_SERVERS 0x04
+# define PXE_DO_NOT_PROMPT 0x08
+# define PXE_DISCOVERY_MCAST_ADDR 7
+# define PXE_BOOT_SERVERS 8
+# define PXE_BOOT_MENU 9
+# define PXE_BOOT_PROMPT 10
+# define PXE_MCAST_ADDRS_ALLOC 11
+# define PXE_CREDENTIAL_TYPES 12
+# define PXE_BOOT_ITEM 71
+#define DHCP4_NBNS_SERVERS 44
+#define DHCP4_NBDD_SERVERS 45
+#define DHCP4_NETBIOS_NODE_TYPE 46
+#define DHCP4_NETBIOS_SCOPE 47
+#define DHCP4_XWINDOW_SYSTEM_FONT_SERVERS 48
+#define DHCP4_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49
+#define DHCP4_REQUESTED_IP_ADDRESS 50
+#define DHCP4_LEASE_TIME 51
+#define DHCP4_OPTION_OVERLOAD 52
+# define DHCP4_OVERLOAD_FNAME 1
+# define DHCP4_OVERLOAD_SNAME 2
+# define DHCP4_OVERLOAD_FNAME_AND_SNAME 3
+#define DHCP4_MESSAGE_TYPE 53
+# define DHCP4_MESSAGE_TYPE_DISCOVER 1
+# define DHCP4_MESSAGE_TYPE_OFFER 2
+# define DHCP4_MESSAGE_TYPE_REQUEST 3
+# define DHCP4_MESSAGE_TYPE_DECLINE 4
+# define DHCP4_MESSAGE_TYPE_ACK 5
+# define DHCP4_MESSAGE_TYPE_NAK 6
+# define DHCP4_MESSAGE_TYPE_RELEASE 7
+# define DHCP4_MESSAGE_TYPE_INFORM 8
+#define DHCP4_SERVER_IDENTIFIER 54
+#define DHCP4_PARAMETER_REQUEST_LIST 55
+#define DHCP4_ERROR_MESSAGE 56
+#define DHCP4_MAX_MESSAGE_SIZE 57
+# define DHCP4_DEFAULT_MAX_MESSAGE_SIZE 576
+#define DHCP4_RENEWAL_TIME 58
+#define DHCP4_REBINDING_TIME 59
+#define DHCP4_CLASS_IDENTIFIER 60
+#define DHCP4_CLIENT_IDENTIFIER 61
+#define DHCP4_NISPLUS_DOMAIN_NAME 64
+#define DHCP4_NISPLUS_SERVERS 65
+#define DHCP4_TFTP_SERVER_NAME 66
+#define DHCP4_BOOTFILE 67
+#define DHCP4_MOBILE_IP_HOME_AGENTS 68
+#define DHCP4_SMPT_SERVERS 69
+#define DHCP4_POP3_SERVERS 70
+#define DHCP4_NNTP_SERVERS 71
+#define DHCP4_WWW_SERVERS 72
+#define DHCP4_FINGER_SERVERS 73
+#define DHCP4_IRC_SERVERS 74
+#define DHCP4_STREET_TALK_SERVERS 75
+#define DHCP4_STREET_TALK_DIR_ASSIST_SERVERS 76
+#define DHCP4_NDS_SERVERS 85
+#define DHCP4_NDS_TREE_NAME 86
+#define DHCP4_NDS_CONTEXT 87
+#define DHCP4_SYSTEM_ARCHITECTURE 93
+#define DHCP4_NETWORK_ARCHITECTURE 94
+#define DHCP4_PLATFORM_ID 97
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+//
+// DHCP4 option format.
+//
+
+#pragma pack(1)
+typedef struct {
+ UINT8 op;
+ UINT8 len;
+ UINT8 data[1];
+} DHCP4_OP;
+#pragma pack()
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+typedef struct {
+ DHCP4_PACKET Discover;
+ DHCP4_PACKET Offer;
+ DHCP4_PACKET Request;
+ DHCP4_PACKET AckNak;
+ BOOLEAN SetupCompleted;
+ BOOLEAN InitCompleted;
+ BOOLEAN SelectCompleted;
+ BOOLEAN IsBootp;
+ BOOLEAN IsAck;
+} EFI_PXE_DHCP4_DATA;
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_DHCP4_RUN) (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN OPTIONAL UINTN OpLen,
+ IN OPTIONAL VOID *OpList
+ );
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_DHCP4_SETUP) (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN OPTIONAL EFI_PXE_DHCP4_DATA * NewData
+ );
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_DHCP4_INIT) (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN SecondsTimeout,
+ OUT UINTN *Offers,
+ OUT DHCP4_PACKET **OfferList
+ );
+
+#define DHCP4_MIN_SECONDS 1
+#define DHCP4_MAX_SECONDS 60
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_DHCP4_SELECT) (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN SecondsTimeout,
+ IN DHCP4_PACKET * offer
+ );
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_DHCP4_RENEW) (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ UINTN seconds_timeout
+ );
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_DHCP4_REBIND) (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ UINTN seconds_timeout
+ );
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_DHCP4_RELEASE) (
+ IN EFI_PXE_DHCP4_PROTOCOL * This
+ );
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#define EFI_PXE_DHCP4_PROTOCOL_REVISION 0x00010000
+
+struct _EFI_PXE_DHCP4_PROTOCOL {
+ UINT64 Revision;
+ EFI_PXE_DHCP4_RUN Run;
+ EFI_PXE_DHCP4_SETUP Setup;
+ EFI_PXE_DHCP4_INIT Init;
+ EFI_PXE_DHCP4_SELECT Select;
+ EFI_PXE_DHCP4_RENEW Renew;
+ EFI_PXE_DHCP4_REBIND Rebind;
+ EFI_PXE_DHCP4_RELEASE Release;
+ EFI_PXE_DHCP4_DATA *Data;
+};
+
+//
+//
+//
+
+extern EFI_GUID gEfiPxeDhcp4ProtocolGuid;
+
+#endif /* _PXEDHCP4_H_ */
+/* EOF - PxeDhcp4.h */
diff --git a/MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h b/MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h
new file mode 100644
index 0000000000..cfba38042a
--- /dev/null
+++ b/MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h
@@ -0,0 +1,85 @@
+/*++
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4Callback.h
+
+Abstract:
+ EFI PXE DHCP4 Callback protocol definition.
+
+--*/
+
+#ifndef _PXE_DHCP4CALLBACK_H
+#define _PXE_DHCP4CALLBACK_H
+
+#include <Protocol/PxeDhcp4.h>
+//
+// GUID definition
+//
+
+#define EFI_PXE_DHCP4_CALLBACK_PROTOCOL_GUID \
+{ 0xc1544c01, 0x92a4, 0x4198, {0x8a, 0x84, 0x77, 0x85, 0x83, 0xc2, 0x36, 0x21 } }
+
+
+//
+// Revision number
+//
+
+#define EFI_PXE_DHCP4_CALLBACK_INTERFACE_REVISION 0x00010000
+
+//
+// Interface definition
+//
+
+typedef struct _EFI_PXE_DHCP4_CALLBACK_PROTOCOL EFI_PXE_DHCP4_CALLBACK_PROTOCOL;
+
+typedef enum {
+ EFI_PXE_DHCP4_FUNCTION_FIRST,
+ EFI_PXE_DHCP4_FUNCTION_INIT,
+ EFI_PXE_DHCP4_FUNCTION_SELECT,
+ EFI_PXE_DHCP4_FUNCTION_RENEW,
+ EFI_PXE_DHCP4_FUNCTION_REBIND,
+ EFI_PXE_DHCP4_FUNCTION_LAST
+} EFI_PXE_DHCP4_FUNCTION;
+
+typedef enum {
+ EFI_PXE_DHCP4_CALLBACK_STATUS_FIRST,
+ EFI_PXE_DHCP4_CALLBACK_STATUS_ABORT,
+ EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_ABORT,
+ EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_ABORT,
+ EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE,
+ EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_CONTINUE,
+ EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE,
+ EFI_PXE_DHCP4_CALLBACK_STATUS_LAST
+} EFI_PXE_DHCP4_CALLBACK_STATUS;
+
+typedef
+EFI_PXE_DHCP4_CALLBACK_STATUS
+(EFIAPI *EFI_PXE_DHCP4_CALLBACK) (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN EFI_PXE_DHCP4_FUNCTION Function,
+ IN UINT32 PacketLen,
+ IN DHCP4_PACKET *Packet OPTIONAL
+ );
+
+struct _EFI_PXE_DHCP4_CALLBACK_PROTOCOL {
+ UINT64 Revision;
+ EFI_PXE_DHCP4_CALLBACK Callback;
+};
+
+//
+// GUID declaration
+//
+
+extern EFI_GUID gEfiPxeDhcp4CallbackProtocolGuid;
+
+#endif /* _PXE_DHCP4CALLBACK_H */
+/* EOF - PxeDhcp4Callback.h */
diff --git a/MdeModulePkg/Include/Protocol/Tcp.h b/MdeModulePkg/Include/Protocol/Tcp.h
new file mode 100644
index 0000000000..f49915cd93
--- /dev/null
+++ b/MdeModulePkg/Include/Protocol/Tcp.h
@@ -0,0 +1,108 @@
+/*++
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ tcp.h
+
+Abstract:
+
+ EFI Transmission Control Protocol
+
+
+
+Revision History
+
+--*/
+
+
+#ifndef _EFITCP_H
+#define _EFITCP_H
+
+
+#include <Protocol/PxeBaseCode.h>
+
+//
+// PXE Base Code protocol
+//
+
+#define EFI_TCP_PROTOCOL_GUID \
+ { 0x02b3d5f2, 0xac28, 0x11d3, { 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }}
+
+
+typedef UINT16 EFI_PXE_BASE_CODE_TCP_PORT;
+
+//
+// Port Receive Filter definitions
+//
+#define EFI_PXE_BASE_CODE_MAX_PORTCNT 8
+typedef struct {
+ UINT8 Filters;
+ UINT8 IpCnt;
+ UINT16 reserved;
+ EFI_IP_ADDRESS IpList[EFI_PXE_BASE_CODE_MAX_PORTCNT];
+} EFI_TCP_PORT_FILTER;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TCP_WRITE) (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN UINT16 *UrgentPointer,
+ IN UINT32 *SequenceNumber,
+ IN UINT32 *AckNumber,
+ IN UINT16 *HlenResCode,
+ IN UINT16 *Window,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN UINT16 *DestPort,
+ IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL
+ IN EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN UINT16 *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TCP_READ) (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL
+ IN OUT UINT16 *DestPort, OPTIONAL
+ IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN OUT UINT16 *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN OUT UINTN *BufferSize,
+ IN VOID *BufferPtr
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TCP_SET_PORT_FILTER) (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_TCP_PORT_FILTER *NewFilter
+ );
+
+//
+// TCP Protocol structure
+//
+typedef struct _EFI_TCP_PROTOCOL {
+ EFI_TCP_WRITE TcpWrite;
+ EFI_TCP_READ TcpRead;
+ EFI_TCP_SET_PORT_FILTER SetPortFilter;
+} EFI_TCP_PROTOCOL;
+
+extern EFI_GUID gEfiTcpProtocolGuid;
+
+#endif /* _EFITCP_H */
diff --git a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c
index dcf2309c71..9057a03a67 100644
--- a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c
+++ b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c
@@ -77,6 +77,7 @@ IP4_ADDR mIp4AllMasks[IP4_MASK_NUM] = {
0xFFFFFFFF,
};
+EFI_IPv4_ADDRESS mZeroIp4Addr = {0, 0, 0, 0};
/**
Converts the low nibble of a byte to hex unicode character.
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 72f58a1fb6..e6a9347969 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -80,6 +80,10 @@
gEfiNicIp4ConfigProtocolGuid = {0xdca3d4d, 0x12da, 0x4728, { 0xbf, 0x7e, 0x86, 0xce, 0xb9, 0x28, 0xd0, 0x67 }}
gEfiNicIp4ConfigVariableGuid = {0xd8944553, 0xc4dd, 0x41f4, { 0x9b, 0x30, 0xe1, 0x39, 0x7c, 0xfb, 0x26, 0x7b }}
+ gEfiTcpProtocolGuid = { 0x02b3d5f2, 0xac28, 0x11d3, { 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }}
+ gEfiPxeDhcp4CallbackProtocolGuid = { 0xc1544c01, 0x92a4, 0x4198, {0x8a, 0x84, 0x77, 0x85, 0x83, 0xc2, 0x36, 0x21 } }
+ gEfiPxeDhcp4ProtocolGuid = { 0x03c4e624, 0xac28, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x29, 0x3f, 0xc1, 0x4d } }
+
[Ppis.common]
gPeiBaseMemoryTestPpiGuid = { 0xB6EC423C, 0x21D2, 0x490D, { 0x85, 0xC6, 0xDD, 0x58, 0x64, 0xEA, 0xA6, 0x74 }}
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index be390f443f..90a97c18a6 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -55,7 +55,7 @@
[LibraryClasses.IPF]
IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
-[LibraryClasses.EBC.DXE_RUNTIME_DRIVER]
+[LibraryClasses.EBC]
IoLib|IntelFrameworkPkg/Library/DxeIoLibCpuIo/DxeIoLibCpuIo.inf
[LibraryClasses.common.PEI_CORE]
@@ -112,6 +112,7 @@
UsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf
NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf
IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf
+ UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
[LibraryClasses.common.DXE_RUNTIME_DRIVER]
HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
@@ -368,8 +369,13 @@
MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf
MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
+ MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf
+ MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf
MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDxe.inf
+ MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf
MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf
+ MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf
+ MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf
MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h
new file mode 100644
index 0000000000..05f542d086
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h
@@ -0,0 +1,30 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ArpDebug.h
+
+Abstract:
+
+
+**/
+
+#ifndef _ARP_DEBUG_H_
+#define _ARP_DEBUG_H_
+
+
+#define ARP_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE ("Arp", PrintArg)
+#define ARP_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Arp", PrintArg)
+#define ARP_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Arp", PrintArg)
+
+#endif
+
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c
new file mode 100644
index 0000000000..2c108574e8
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c
@@ -0,0 +1,763 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ArpDriver.c
+
+Abstract:
+
+
+**/
+
+
+#include "ArpDriver.h"
+#include "ArpImpl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gArpDriverBinding = {
+ ArpDriverBindingSupported,
+ ArpDriverBindingStart,
+ ArpDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+
+/**
+ Create and initialize the arp service context data.
+
+ @param ImageHandle The image handle representing the loaded driver
+ image.
+ @param ControllerHandle The controller handle the driver binds to.
+ @param ArpService Pointer to the buffer containing the arp service
+ context data.
+
+ @retval EFI_SUCCESS The arp service context is initialized.
+ @retval other Failed to initialize the arp service context.
+
+**/
+STATIC
+EFI_STATUS
+ArpCreateService (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle,
+ IN ARP_SERVICE_DATA *ArpService
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (ArpService != NULL);
+
+ ArpService->Signature = ARP_SERVICE_DATA_SIGNATURE;
+
+ //
+ // Init the servicebinding protocol members.
+ //
+ ArpService->ServiceBinding.CreateChild = ArpServiceBindingCreateChild;
+ ArpService->ServiceBinding.DestroyChild = ArpServiceBindingDestroyChild;
+
+ //
+ // Save the handles.
+ //
+ ArpService->ImageHandle = ImageHandle;
+ ArpService->ControllerHandle = ControllerHandle;
+
+ //
+ // Create a MNP child instance.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ ImageHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ &ArpService->MnpChildHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Open the MNP protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **)&ArpService->Mnp,
+ ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Get the underlayer Snp mode data.
+ //
+ Status = ArpService->Mnp->GetModeData (ArpService->Mnp, NULL, &ArpService->SnpMode);
+ if ((Status != EFI_NOT_STARTED) && EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ if (ArpService->SnpMode.IfType != NET_IFTYPE_ETHERNET) {
+ //
+ // Only support the ethernet.
+ //
+ Status = EFI_UNSUPPORTED;
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Set the Mnp config parameters.
+ //
+ ArpService->MnpConfigData.ReceivedQueueTimeoutValue = 0;
+ ArpService->MnpConfigData.TransmitQueueTimeoutValue = 0;
+ ArpService->MnpConfigData.ProtocolTypeFilter = ARP_ETHER_PROTO_TYPE;
+ ArpService->MnpConfigData.EnableUnicastReceive = TRUE;
+ ArpService->MnpConfigData.EnableMulticastReceive = FALSE;
+ ArpService->MnpConfigData.EnableBroadcastReceive = TRUE;
+ ArpService->MnpConfigData.EnablePromiscuousReceive = FALSE;
+ ArpService->MnpConfigData.FlushQueuesOnReset = TRUE;
+ ArpService->MnpConfigData.EnableReceiveTimestamps = FALSE;
+ ArpService->MnpConfigData.DisableBackgroundPolling = FALSE;
+
+ //
+ // Configure the Mnp child.
+ //
+ Status = ArpService->Mnp->Configure (ArpService->Mnp, &ArpService->MnpConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Create the event used in the RxToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ NET_TPL_EVENT,
+ ArpOnFrameRcvd,
+ ArpService,
+ &ArpService->RxToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Create the Arp heartbeat timer.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ NET_TPL_TIMER,
+ ArpTimerHandler,
+ ArpService,
+ &ArpService->PeriodicTimer
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Start the heartbeat timer.
+ //
+ Status = gBS->SetTimer (
+ ArpService->PeriodicTimer,
+ TimerPeriodic,
+ ARP_PERIODIC_TIMER_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_EXIT;
+ }
+
+ //
+ // Init the lock.
+ //
+ NET_LOCK_INIT (&ArpService->Lock);
+
+ //
+ // Init the lists.
+ //
+ NetListInit (&ArpService->ChildrenList);
+ NetListInit (&ArpService->PendingRequestTable);
+ NetListInit (&ArpService->DeniedCacheTable);
+ NetListInit (&ArpService->ResolvedCacheTable);
+
+ERROR_EXIT:
+
+ return Status;
+}
+
+
+/**
+ Clean the arp service context data.
+
+ @param ArpService Pointer to the buffer containing the arp service
+ context data.
+
+ @return None.
+
+**/
+STATIC
+VOID
+ArpCleanService (
+ IN ARP_SERVICE_DATA *ArpService
+ )
+{
+ NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);
+
+ if (ArpService->PeriodicTimer != NULL) {
+ //
+ // Cancle and close the PeriodicTimer.
+ //
+ gBS->SetTimer (ArpService->PeriodicTimer, TimerCancel, 0);
+ gBS->CloseEvent (ArpService->PeriodicTimer);
+ }
+
+ if (ArpService->RxToken.Event != NULL) {
+ //
+ // Cancle the RxToken and close the event in the RxToken.
+ //
+ ArpService->Mnp->Cancel (ArpService->Mnp, NULL);
+ gBS->CloseEvent (ArpService->RxToken.Event);
+ }
+
+ if (ArpService->Mnp != NULL) {
+ //
+ // Reset the Mnp child and close the Mnp protocol.
+ //
+ ArpService->Mnp->Configure (ArpService->Mnp, NULL);
+ gBS->CloseProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ ArpService->ImageHandle,
+ ArpService->ControllerHandle
+ );
+ }
+
+ if (ArpService->MnpChildHandle != NULL) {
+ //
+ // Destroy the mnp child.
+ //
+ NetLibDestroyServiceChild(
+ ArpService->ControllerHandle,
+ ArpService->ImageHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ ArpService->MnpChildHandle
+ );
+ }
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver supports this device
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test to see if Arp SB is already installed.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test to see if MNP SB is installed.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+
+ //
+ // Allocate a zero pool for ArpService.
+ //
+ ArpService = NetAllocateZeroPool (sizeof(ARP_SERVICE_DATA));
+ if (ArpService == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the arp service context data.
+ //
+ Status = ArpCreateService (This->DriverBindingHandle, ControllerHandle, ArpService);
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ //
+ // Install the ARP service binding protocol.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ &ArpService->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ //
+ // OK, start to receive arp packets from Mnp.
+ //
+ Status = ArpService->Mnp->Receive (ArpService->Mnp, &ArpService->RxToken);
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ return Status;
+
+ERROR:
+
+ //
+ // On error, clean the arp service context data, and free the memory allocated.
+ //
+ ArpCleanService (ArpService);
+ NetFreePool (ArpService);
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero stop the entire bus driver.
+ @param ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCES This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ ARP_SERVICE_DATA *ArpService;
+ ARP_INSTANCE_DATA *Instance;
+
+ //
+ // Get the NicHandle which the arp servicebinding is installed on.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Try to get the arp servicebinding protocol on the NicHandle.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ (VOID **)&ServiceBinding,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ ARP_DEBUG_ERROR (("ArpDriverBindingStop: Open ArpSb failed, %r.\n", Status));
+ return Status;
+ }
+
+ ArpService = ARP_SERVICE_DATA_FROM_THIS (ServiceBinding);
+
+ while (!NetListIsEmpty (&ArpService->ChildrenList)) {
+ //
+ // Iterate all the instances.
+ //
+ Instance = NET_LIST_HEAD (&ArpService->ChildrenList, ARP_INSTANCE_DATA, List);
+
+ //
+ // Destroy this arp child.
+ //
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+ }
+
+ ASSERT (NetListIsEmpty (&ArpService->PendingRequestTable));
+ ASSERT (NetListIsEmpty (&ArpService->DeniedCacheTable));
+ ASSERT (NetListIsEmpty (&ArpService->ResolvedCacheTable));
+
+ //
+ // Uninstall the ARP ServiceBinding protocol.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ &ArpService->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ARP_DEBUG_ERROR (("ArpDriverBindingStop: Failed to uninstall ArpSb, %r.\n", Status));
+ return Status;
+ }
+
+ //
+ // Clean the arp servicebinding context data and free the memory allocated.
+ //
+ ArpCleanService (ArpService);
+ NetFreePool (ArpService);
+
+ return Status;
+}
+
+
+/**
+ Creates a child handle with a set of I/O services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If it is
+ not NULL, then the I/O services are added to the
+ existing child handle.
+
+ @retval EFI_SUCCES The child handle was created with the I/O
+ services.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+ ARP_INSTANCE_DATA *Instance;
+ VOID *Mnp;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ArpService = ARP_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Allocate memory for the instance context data.
+ //
+ Instance = NetAllocateZeroPool (sizeof(ARP_INSTANCE_DATA));
+ if (Instance == NULL) {
+ ARP_DEBUG_ERROR (("ArpSBCreateChild: Failed to allocate memory for Instance.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Init the instance context data.
+ //
+ ArpInitInstance (ArpService, Instance);
+
+ //
+ // Install the ARP protocol onto the ChildHandle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiArpProtocolGuid,
+ (VOID *)&Instance->ArpProto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ARP_DEBUG_ERROR (("ArpSBCreateChild: faild to install ARP protocol, %r.\n", Status));
+
+ NetFreePool (Instance);
+ return Status;
+ }
+
+ //
+ // Save the ChildHandle.
+ //
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Managed Network protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &Mnp,
+ gArpDriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto ERROR;
+ }
+
+ //
+ // Insert the instance into children list managed by the arp service context data.
+ //
+ NetListInsertTail (&ArpService->ChildrenList, &Instance->List);
+ ArpService->ChildrenNumber++;
+
+ NET_UNLOCK (&ArpService->Lock);
+
+ERROR:
+
+ if (EFI_ERROR (Status)) {
+
+ gBS->CloseProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ gArpDriverBinding.DriverBindingHandle,
+ Instance->Handle
+ );
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiArpProtocolGuid,
+ &Instance->ArpProto,
+ NULL
+ );
+
+ //
+ // Free the allocated memory.
+ //
+ NetFreePool (Instance);
+ }
+
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a set of I/O services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The I/O services were removed from the child
+ handle.
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services
+ that are being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its I/O services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+ ARP_INSTANCE_DATA *Instance;
+ EFI_ARP_PROTOCOL *Arp;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ArpService = ARP_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Get the arp protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiArpProtocolGuid,
+ (VOID **)&Arp,
+ ArpService->ImageHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (Arp);
+
+ if (Instance->Destroyed) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Use the Destroyed as a flag to avoid re-entrance.
+ //
+ Instance->Destroyed = TRUE;
+
+ //
+ // Close the Managed Network protocol.
+ //
+ gBS->CloseProtocol (
+ ArpService->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ gArpDriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the ARP protocol.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiArpProtocolGuid,
+ &Instance->ArpProto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ ARP_DEBUG_ERROR (("ArpSBDestroyChild: Failed to uninstall the arp protocol, %r.\n",
+ Status));
+
+ Instance->Destroyed = FALSE;
+ return Status;
+ }
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ Instance->Destroyed = FALSE;
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (Instance->Configured) {
+ //
+ // Delete the related cache entry.
+ //
+ ArpDeleteCacheEntry (Instance, FALSE, NULL, TRUE);
+
+ //
+ // Reset the instance configuration.
+ //
+ ArpConfigureInstance (Instance, NULL);
+ }
+
+ //
+ // Remove this instance from the ChildrenList.
+ //
+ NetListRemoveEntry (&Instance->List);
+ ArpService->ChildrenNumber--;
+
+ NET_UNLOCK (&ArpService->Lock);
+
+ NetFreePool (Instance);
+
+ return Status;
+}
+
+//@MT: EFI_DRIVER_ENTRY_POINT (ArpDriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+ArpDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ The entry point for Arp driver which installs the driver binding and component name
+ protocol on its ImageHandle.
+
+Arguments:
+
+ ImageHandle - The image handle of the driver.
+ SystemTable - The system table.
+
+Returns:
+
+ EFI_SUCCESS - if the driver binding and component name protocols are successfully
+ installed, otherwise if failed.
+
+--*/
+{
+ return NetLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gArpDriverBinding,
+ ImageHandle,
+ &gArpComponentName,
+ NULL,
+ NULL
+ );
+}
+
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h
new file mode 100644
index 0000000000..93526c777f
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h
@@ -0,0 +1,84 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ArpDriver.c
+
+Abstract:
+
+
+**/
+
+#ifndef _ARP_DRIVER_H_
+#define _ARP_DRIVER_H_
+
+
+#include <PiDxe.h>
+
+#include <Protocol/Arp.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include "ArpDebug.h"
+
+//
+// Global variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gArpDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gArpComponentName;
+
+EFI_STATUS
+EFIAPI
+ArpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+ArpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+ArpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+EFI_STATUS
+EFIAPI
+ArpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+EFI_STATUS
+EFIAPI
+ArpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
+
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf
new file mode 100644
index 0000000000..4cc9b2e533
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf
@@ -0,0 +1,59 @@
+#/** @file
+# Component description file for ARP module
+#
+# Copyright (c) 2006, Intel Corporation
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = ArpDxe
+ FILE_GUID = 529D3F93-E8E9-4e73-B1E1-BDF6A9D50113
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = ArpDriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ ArpMain.c
+ ArpDriver.h
+ ComponentName.c
+ ArpImpl.h
+ ArpImpl.c
+ ArpDebug.h
+ ArpDriver.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+
+
+[Protocols]
+ gEfiManagedNetworkServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiArpServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiManagedNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiArpProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa
new file mode 100644
index 0000000000..89178fa286
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa
@@ -0,0 +1,72 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>Arp</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>529D3F93-E8E9-4e73-B1E1-BDF6A9D50113</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Arp</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>Arp</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>ArpDriver.c</Filename>
+ <Filename>ArpDebug.h</Filename>
+ <Filename>ArpImpl.c</Filename>
+ <Filename>ArpImpl.h</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>ArpDriver.h</Filename>
+ <Filename>ArpMain.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiArpProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiManagedNetworkProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiArpServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiManagedNetworkServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>ArpDriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c
new file mode 100644
index 0000000000..93b4c10200
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c
@@ -0,0 +1,1628 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ArpImpl.c
+
+Abstract:
+
+
+**/
+
+
+#include "ArpImpl.h"
+#include "ArpDebug.h"
+
+EFI_ARP_PROTOCOL mEfiArpProtocolTemplate = {
+ ArpConfigure,
+ ArpAdd,
+ ArpFind,
+ ArpDelete,
+ ArpFlush,
+ ArpRequest,
+ ArpCancel
+};
+
+
+/**
+ Initialize the instance context data.
+
+ @param ArpService Pointer to the arp service context data this
+ instance belongs to.
+ @param Instance Pointer to the instance context data.
+
+ @return None.
+
+**/
+VOID
+ArpInitInstance (
+ IN ARP_SERVICE_DATA *ArpService,
+ IN ARP_INSTANCE_DATA *Instance
+ )
+{
+ NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);
+
+ Instance->Signature = ARP_INSTANCE_DATA_SIGNATURE;
+ Instance->ArpService = ArpService;
+
+ CopyMem (&Instance->ArpProto, &mEfiArpProtocolTemplate, sizeof (ARP_SERVICE_DATA));
+
+ Instance->Configured = FALSE;
+ Instance->Destroyed = FALSE;
+
+ NetListInit (&Instance->List);
+}
+
+
+/**
+ Process the Arp packets received from Mnp, the procedure conforms to RFC826.
+
+ @param Event The Event this notify function registered to.
+ @param Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameRcvd (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *RxToken;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData;
+ ARP_HEAD *Head;
+ ARP_ADDRESS ArpAddress;
+ ARP_CACHE_ENTRY *CacheEntry;
+ NET_LIST_ENTRY *Entry;
+ ARP_INSTANCE_DATA *Instance;
+ EFI_ARP_CONFIG_DATA *ConfigData;
+ NET_ARP_ADDRESS SenderAddress[2];
+ BOOLEAN ProtoMatched;
+ BOOLEAN IsTarget;
+ BOOLEAN MergeFlag;
+
+ ArpService = (ARP_SERVICE_DATA *)Context;
+ NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);
+
+ RxToken = &ArpService->RxToken;
+
+ if (RxToken->Status == EFI_ABORTED) {
+ //
+ // The Token is aborted, possibly by arp itself, just return and the receiving
+ // process is stopped.
+ //
+ return;
+ }
+
+ if (EFI_ERROR (RxToken->Status)) {
+ //
+ // Restart the receiving if any other error Status occurs.
+ //
+ goto RESTART_RECEIVE;
+ }
+
+ //
+ // Status is EFI_SUCCESS, process the received frame.
+ //
+ RxData = RxToken->Packet.RxData;
+ Head = (ARP_HEAD *) RxData->PacketData;
+
+ //
+ // Convert the byte order of the multi-byte fields.
+ //
+ Head->HwType = NTOHS (Head->HwType);
+ Head->ProtoType = NTOHS (Head->ProtoType);
+ Head->OpCode = NTOHS (Head->OpCode);
+
+ if ((Head->HwType != ArpService->SnpMode.IfType) ||
+ (Head->HwAddrLen != ArpService->SnpMode.HwAddressSize) ||
+ (RxData->ProtocolType != ARP_ETHER_PROTO_TYPE)) {
+ //
+ // The hardware type or the hardware address length doesn't match.
+ // There is a sanity check for the protocol type too.
+ //
+ goto RECYCLE_RXDATA;
+ }
+
+ //
+ // Set the pointers to the addresses contained in the arp packet.
+ //
+ ArpAddress.SenderHwAddr = (UINT8 *)(Head + 1);
+ ArpAddress.SenderProtoAddr = ArpAddress.SenderHwAddr + Head->HwAddrLen;
+ ArpAddress.TargetHwAddr = ArpAddress.SenderProtoAddr + Head->ProtoAddrLen;
+ ArpAddress.TargetProtoAddr = ArpAddress.TargetHwAddr + Head->HwAddrLen;
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ ARP_DEBUG_ERROR (("ArpOnFrameRcvd: Faild to acquire the CacheTableLock.\n"));
+ goto RECYCLE_RXDATA;
+ }
+
+ SenderAddress[Hardware].Type = Head->HwType;
+ SenderAddress[Hardware].Length = Head->HwAddrLen;
+ SenderAddress[Hardware].AddressPtr = ArpAddress.SenderHwAddr;
+
+ SenderAddress[Protocol].Type = Head->ProtoType;
+ SenderAddress[Protocol].Length = Head->ProtoAddrLen;
+ SenderAddress[Protocol].AddressPtr = ArpAddress.SenderProtoAddr;
+
+ //
+ // First, check the denied cache table.
+ //
+ CacheEntry = ArpFindDeniedCacheEntry (
+ ArpService,
+ &SenderAddress[Protocol],
+ &SenderAddress[Hardware]
+ );
+ if (CacheEntry != NULL) {
+ //
+ // This address (either hardware or protocol address, or both) is configured to
+ // be a deny entry, silently skip the normal process.
+ //
+ goto UNLOCK_EXIT;
+ }
+
+ ProtoMatched = FALSE;
+ IsTarget = FALSE;
+ Instance = NULL;
+ NET_LIST_FOR_EACH (Entry, &ArpService->ChildrenList) {
+ //
+ // Iterate all the children.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, ARP_INSTANCE_DATA, List);
+ NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);
+ ConfigData = &Instance->ConfigData;
+
+ if ((Instance->Configured) &&
+ (Head->ProtoType == ConfigData->SwAddressType) &&
+ (Head->ProtoAddrLen == ConfigData->SwAddressLength)) {
+ //
+ // The protocol type is matched for the received arp packet.
+ //
+ ProtoMatched = TRUE;
+ if (0 == NetCompareMem (
+ (VOID *)ArpAddress.TargetProtoAddr,
+ ConfigData->StationAddress,
+ ConfigData->SwAddressLength
+ )) {
+ //
+ // The arp driver has the target address required by the received arp packet.
+ //
+ IsTarget = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!ProtoMatched) {
+ //
+ // Protocol type unmatchable, skip.
+ //
+ goto UNLOCK_EXIT;
+ }
+
+ //
+ // Check whether the sender's address information is already in the cache.
+ //
+ MergeFlag = FALSE;
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ NULL,
+ ByProtoAddress,
+ &SenderAddress[Protocol],
+ NULL
+ );
+ if (CacheEntry != NULL) {
+ //
+ // Update the entry with the new information.
+ //
+ ArpFillAddressInCacheEntry (CacheEntry, &SenderAddress[Hardware], NULL);
+ CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;
+ MergeFlag = TRUE;
+ }
+
+ if (!IsTarget) {
+ //
+ // This arp packet isn't targeted to us, skip now.
+ //
+ goto UNLOCK_EXIT;
+ }
+
+ if (!MergeFlag) {
+ //
+ // Add the triplet <protocol type, sender protocol address, sender hardware address>
+ // to the translation table.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->PendingRequestTable,
+ NULL,
+ ByProtoAddress,
+ &SenderAddress[Protocol],
+ NULL
+ );
+ if (CacheEntry == NULL) {
+ //
+ // Allocate a new CacheEntry.
+ //
+ CacheEntry = ArpAllocCacheEntry (NULL);
+ if (CacheEntry == NULL) {
+ goto UNLOCK_EXIT;
+ }
+ }
+
+ NetListRemoveEntry (&CacheEntry->List);
+
+ //
+ // Fill the addresses into the CacheEntry.
+ //
+ ArpFillAddressInCacheEntry (
+ CacheEntry,
+ &SenderAddress[Hardware],
+ &SenderAddress[Protocol]
+ );
+
+ //
+ // Inform the user.
+ //
+ ArpAddressResolved (CacheEntry, NULL, NULL);
+
+ //
+ // Add this entry into the ResolvedCacheTable
+ //
+ NetListInsertHead (&ArpService->ResolvedCacheTable, &CacheEntry->List);
+ }
+
+ if (Head->OpCode == ARP_OPCODE_REQUEST) {
+ //
+ // Send back the ARP Reply. If we reach here, Instance is not NULL and CacheEntry
+ // is not NULL.
+ //
+ ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REPLY);
+ }
+
+UNLOCK_EXIT:
+
+ NET_UNLOCK (&ArpService->Lock);
+
+RECYCLE_RXDATA:
+
+ //
+ // Signal Mnp to recycle the RxData.
+ //
+ gBS->SignalEvent (RxData->RecycleEvent);
+
+RESTART_RECEIVE:
+
+ //
+ // Continue to receive packets from Mnp.
+ //
+ Status = ArpService->Mnp->Receive (ArpService->Mnp, RxToken);
+
+ DEBUG_CODE (
+ if (EFI_ERROR (Status)) {
+ ARP_DEBUG_ERROR (("ArpOnFrameRcvd: ArpService->Mnp->Receive "
+ "failed, %r\n.", Status));
+ }
+ );
+}
+
+
+/**
+ Process the already sent arp packets.
+
+ @param Event The Event this notify function registered to.
+ @param Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpOnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TxToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
+
+ ASSERT (Context != NULL);
+
+ TxToken = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *)Context;
+ TxData = TxToken->Packet.TxData;
+
+ DEBUG_CODE (
+ if (EFI_ERROR (TxToken->Status)) {
+ ARP_DEBUG_ERROR (("ArpOnFrameSent: TxToken->Status, %r.\n", TxToken->Status));
+ }
+ );
+
+ //
+ // Free the allocated memory and close the event.
+ //
+ NetFreePool (TxData->FragmentTable[0].FragmentBuffer);
+ NetFreePool (TxData);
+ gBS->CloseEvent (TxToken->Event);
+ NetFreePool (TxToken);
+}
+
+
+/**
+ Process the arp cache olding and drive the retrying arp requests.
+
+ @param Event The Event this notify function registered to.
+ @param Context Pointer to the context data registerd to the
+ Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+ArpTimerHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ ARP_SERVICE_DATA *ArpService;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *NextEntry;
+ NET_LIST_ENTRY *ContextEntry;
+ ARP_CACHE_ENTRY *CacheEntry;
+ USER_REQUEST_CONTEXT *RequestContext;
+
+ ASSERT (Context != NULL);
+ ArpService = (ARP_SERVICE_DATA *)Context;
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ return;
+ }
+
+ //
+ // Iterate all the pending requests to see whether a retry is needed to send out
+ // or the request finally fails because the retry time reaches the limitation.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+
+ if (CacheEntry->NextRetryTime <= ARP_PERIODIC_TIMER_INTERVAL) {
+ //
+ // Timeout, if we can retry more, send out the request again, otherwise abort
+ // this request.
+ //
+ if (CacheEntry->RetryCount == 0) {
+ //
+ // Abort this request.
+ //
+ ArpAddressResolved (CacheEntry, NULL, NULL);
+ ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList));
+
+ NetListRemoveEntry (&CacheEntry->List);
+ NetFreePool (CacheEntry);
+ } else {
+ //
+ // resend the ARP request.
+ //
+ ASSERT (!NetListIsEmpty(&CacheEntry->UserRequestList));
+
+ ContextEntry = CacheEntry->UserRequestList.ForwardLink;
+ RequestContext = NET_LIST_USER_STRUCT (ContextEntry, USER_REQUEST_CONTEXT, List);
+
+ ArpSendFrame (RequestContext->Instance, CacheEntry, ARP_OPCODE_REQUEST);
+
+ CacheEntry->RetryCount--;
+ CacheEntry->NextRetryTime = RequestContext->Instance->ConfigData.RetryTimeOut;
+ }
+ } else {
+ //
+ // Update the NextRetryTime.
+ //
+ CacheEntry->NextRetryTime -= ARP_PERIODIC_TIMER_INTERVAL;
+ }
+ }
+
+ //
+ // Check the timeouts for the DeniedCacheTable.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->DeniedCacheTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+ ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList));
+
+ if (CacheEntry->DefaultDecayTime == 0) {
+ //
+ // It's a static entry, skip it.
+ //
+ continue;
+ }
+
+ if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) {
+ //
+ // Time out, remove it.
+ //
+ NetListRemoveEntry (&CacheEntry->List);
+ NetFreePool (CacheEntry);
+ } else {
+ //
+ // Update the DecayTime.
+ //
+ CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL;
+ }
+ }
+
+ //
+ // Check the timeouts for the ResolvedCacheTable.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->ResolvedCacheTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+ ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList));
+
+ if (CacheEntry->DefaultDecayTime == 0) {
+ //
+ // It's a static entry, skip it.
+ //
+ continue;
+ }
+
+ if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) {
+ //
+ // Time out, remove it.
+ //
+ NetListRemoveEntry (&CacheEntry->List);
+ NetFreePool (CacheEntry);
+ } else {
+ //
+ // Update the DecayTime.
+ //
+ CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL;
+ }
+ }
+
+ NET_UNLOCK (&ArpService->Lock);
+}
+
+
+/**
+ Match the two NET_ARP_ADDRESSes.
+
+ @param AddressOne Pointer to the first address to match.
+ @param AddressTwo Pointer to the second address to match.
+
+ @return The two addresses match or not.
+
+**/
+STATIC
+BOOLEAN
+ArpMatchAddress (
+ IN NET_ARP_ADDRESS *AddressOne,
+ IN NET_ARP_ADDRESS *AddressTwo
+ )
+{
+ if ((AddressOne->Type != AddressTwo->Type) ||
+ (AddressOne->Length != AddressTwo->Length)) {
+ //
+ // Either Type or Length doesn't match.
+ //
+ return FALSE;
+ }
+
+ if ((AddressOne->AddressPtr != NULL) &&
+ (NetCompareMem (
+ AddressOne->AddressPtr,
+ AddressTwo->AddressPtr,
+ AddressOne->Length
+ ) != 0)) {
+ //
+ // The address is not the same.
+ //
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Find the CacheEntry which matches the requirements in the specified CacheTable.
+
+ @param CacheTable Pointer to the arp cache table.
+ @param StartEntry Pointer to the start entry this search begins with
+ in the cache table.
+ @param FindOpType The search type.
+ @param ProtocolAddress Pointer to the protocol address to match.
+ @param HardwareAddress Pointer to the hardware address to match.
+
+ @return Pointer to the matched arp cache entry, if NULL, no match is found.
+
+**/
+ARP_CACHE_ENTRY *
+ArpFindNextCacheEntryInTable (
+ IN NET_LIST_ENTRY *CacheTable,
+ IN NET_LIST_ENTRY *StartEntry,
+ IN FIND_OPTYPE FindOpType,
+ IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL,
+ IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ ARP_CACHE_ENTRY *CacheEntry;
+
+ if (StartEntry == NULL) {
+ //
+ // Start from the beginning of the table if no StartEntry is specified.
+ //
+ StartEntry = CacheTable;
+ }
+
+ for (Entry = StartEntry->ForwardLink; Entry != CacheTable; Entry = Entry->ForwardLink) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+
+ if (FindOpType & MATCH_SW_ADDRESS) {
+ //
+ // Find by the software address.
+ //
+ if (!ArpMatchAddress (ProtocolAddress, &CacheEntry->Addresses[Protocol])) {
+ //
+ // The ProtocolAddress doesn't match, continue to the next cache entry.
+ //
+ continue;
+ }
+ }
+
+ if (FindOpType & MATCH_HW_ADDRESS) {
+ //
+ // Find by the hardware address.
+ //
+ if (!ArpMatchAddress (HardwareAddress, &CacheEntry->Addresses[Hardware])) {
+ //
+ // The HardwareAddress doesn't match, continue to the next cache entry.
+ //
+ continue;
+ }
+ }
+
+ //
+ // The CacheEntry meets the requirements now, return this entry.
+ //
+ return CacheEntry;
+ }
+
+ //
+ // No matching.
+ //
+ return NULL;
+}
+
+
+/**
+ Find the CacheEntry, using ProtocolAddress or HardwareAddress or both, as the keyword,
+ in the DeniedCacheTable.
+
+ @param ArpService Pointer to the arp service context data.
+ @param ProtocolAddress Pointer to the protocol address.
+ @param HardwareAddress Pointer to the hardware address.
+
+ @return Pointer to the matched cache entry, if NULL no match is found.
+
+**/
+ARP_CACHE_ENTRY *
+ArpFindDeniedCacheEntry (
+ IN ARP_SERVICE_DATA *ArpService,
+ IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL,
+ IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL
+ )
+{
+ ARP_CACHE_ENTRY *CacheEntry;
+
+ ASSERT ((ProtocolAddress != NULL) || (HardwareAddress != NULL));
+ NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);
+
+ CacheEntry = NULL;
+
+ if ((ProtocolAddress != NULL) && (ProtocolAddress->AddressPtr != NULL)) {
+ //
+ // Find the cache entry in the DeniedCacheTable by the protocol address.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->DeniedCacheTable,
+ NULL,
+ ByProtoAddress,
+ ProtocolAddress,
+ NULL
+ );
+ if (CacheEntry != NULL) {
+ //
+ // There is a match.
+ //
+ return CacheEntry;
+ }
+ }
+
+ if ((HardwareAddress != NULL) && (HardwareAddress->AddressPtr != NULL)) {
+ //
+ // Find the cache entry in the DeniedCacheTable by the hardware address.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->DeniedCacheTable,
+ NULL,
+ ByHwAddress,
+ NULL,
+ HardwareAddress
+ );
+ }
+
+ return CacheEntry;
+}
+
+
+/**
+ Allocate a cache entry and initialize it.
+
+ @param Instance Pointer to the instance context data.
+
+ @return Pointer to the new created cache entry.
+
+**/
+ARP_CACHE_ENTRY *
+ArpAllocCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance
+ )
+{
+ ARP_CACHE_ENTRY *CacheEntry;
+ NET_ARP_ADDRESS *Address;
+ UINT16 Index;
+
+ //
+ // Allocate memory for the cache entry.
+ //
+ CacheEntry = NetAllocatePool (sizeof (ARP_CACHE_ENTRY));
+ if (CacheEntry == NULL) {
+ return NULL;
+ }
+
+ //
+ // Init the lists.
+ //
+ NetListInit (&CacheEntry->List);
+ NetListInit (&CacheEntry->UserRequestList);
+
+ for (Index = 0; Index < 2; Index++) {
+ //
+ // Init the address pointers to point to the concrete buffer.
+ //
+ Address = &CacheEntry->Addresses[Index];
+ Address->AddressPtr = Address->Buffer.ProtoAddress;
+ }
+
+ //
+ // Zero the hardware address first.
+ //
+ NetZeroMem (CacheEntry->Addresses[Hardware].AddressPtr, ARP_MAX_HARDWARE_ADDRESS_LEN);
+
+ if (Instance != NULL) {
+ //
+ // Inherit the parameters from the instance configuration.
+ //
+ CacheEntry->RetryCount = Instance->ConfigData.RetryCount;
+ CacheEntry->NextRetryTime = Instance->ConfigData.RetryTimeOut;
+ CacheEntry->DefaultDecayTime = Instance->ConfigData.EntryTimeOut;
+ CacheEntry->DecayTime = Instance->ConfigData.EntryTimeOut;
+ } else {
+ //
+ // Use the default parameters if this cache entry isn't allocate in a
+ // instance's scope.
+ //
+ CacheEntry->RetryCount = ARP_DEFAULT_RETRY_COUNT;
+ CacheEntry->NextRetryTime = ARP_DEFAULT_RETRY_INTERVAL;
+ CacheEntry->DefaultDecayTime = ARP_DEFAULT_TIMEOUT_VALUE;
+ CacheEntry->DecayTime = ARP_DEFAULT_TIMEOUT_VALUE;
+ }
+
+ return CacheEntry;
+}
+
+
+/**
+ Turn the CacheEntry into the resolved status.
+
+ @param CacheEntry Pointer to the resolved cache entry.
+ @param Instance Pointer to the instance context data.
+ @param UserEvent Pointer to the UserEvent to notify.
+
+ @return The count of notifications sent to the instance.
+
+**/
+UINTN
+ArpAddressResolved (
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN ARP_INSTANCE_DATA *Instance OPTIONAL,
+ IN EFI_EVENT UserEvent OPTIONAL
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *NextEntry;
+ USER_REQUEST_CONTEXT *Context;
+ UINTN Count;
+
+ Count = 0;
+
+ //
+ // Iterate all the linked user requests to notify them.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &CacheEntry->UserRequestList) {
+ Context = NET_LIST_USER_STRUCT (Entry, USER_REQUEST_CONTEXT, List);
+
+ if (((Instance == NULL) || (Context->Instance == Instance)) &&
+ ((UserEvent == NULL) || (Context->UserRequestEvent == UserEvent))) {
+ //
+ // Copy the address to the user-provided buffer and notify the user.
+ //
+ NetCopyMem (
+ Context->UserHwAddrBuffer,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ CacheEntry->Addresses[Hardware].Length
+ );
+ gBS->SignalEvent (Context->UserRequestEvent);
+
+ //
+ // Remove this user request and free the context data.
+ //
+ NetListRemoveEntry (&Context->List);
+ NetFreePool (Context);
+
+ Count++;
+ }
+ }
+
+ return Count;
+}
+
+
+/**
+ Fill the addresses in the CacheEntry using the information passed in by
+ HwAddr and SwAddr.
+
+ @param CacheEntry Pointer to the cache entry.
+ @param HwAddr Pointer to the software address.
+ @param SwAddr Pointer to the hardware address.
+
+ @return None.
+
+**/
+VOID
+ArpFillAddressInCacheEntry (
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN NET_ARP_ADDRESS *HwAddr OPTIONAL,
+ IN NET_ARP_ADDRESS *SwAddr OPTIONAL
+ )
+{
+ NET_ARP_ADDRESS *Address[2];
+ NET_ARP_ADDRESS *CacheAddress;
+ UINT32 Index;
+
+ Address[Hardware] = HwAddr;
+ Address[Protocol] = SwAddr;
+
+ for (Index = 0; Index < 2; Index++) {
+ if (Address[Index] != NULL) {
+ //
+ // Fill the address if the passed in pointer is not NULL.
+ //
+ CacheAddress = &CacheEntry->Addresses[Index];
+
+ CacheAddress->Type = Address[Index]->Type;
+ CacheAddress->Length = Address[Index]->Length;
+
+ if (Address[Index]->AddressPtr != NULL) {
+ //
+ // Copy it if the AddressPtr points to some buffer.
+ //
+ NetCopyMem (
+ CacheAddress->AddressPtr,
+ Address[Index]->AddressPtr,
+ CacheAddress->Length
+ );
+ } else {
+ //
+ // Zero the corresponding address buffer in the CacheEntry.
+ //
+ NetZeroMem (CacheAddress->AddressPtr, CacheAddress->Length);
+ }
+ }
+ }
+}
+
+
+/**
+ Configure the instance using the ConfigData. ConfigData is already validated.
+
+ @param Instance Pointer to the instance context data to be
+ configured.
+ @param ConfigData Pointer to the configuration data used to
+ configure the instance.
+
+ @retval EFI_SUCCESS The instance is configured with the ConfigData.
+ @retval EFI_ACCESS_DENIED The instance is already configured and the
+ ConfigData tries to reset some unchangeable
+ fields.
+ @retval EFI_INVALID_PARAMETER The ConfigData provides a non-unicast IPv4 address
+ when the SwAddressType is IPv4.
+ @retval EFI_OUT_OF_RESOURCES The instance fails to configure due to memory
+ limitation.
+
+**/
+EFI_STATUS
+ArpConfigureInstance (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL
+ )
+{
+ EFI_ARP_CONFIG_DATA *OldConfigData;
+ IP4_ADDR Ip;
+
+ OldConfigData = &Instance->ConfigData;
+
+ if (ConfigData != NULL) {
+
+ if (Instance->Configured) {
+ //
+ // The instance is configured, check the unchangeable fields.
+ //
+ if ((OldConfigData->SwAddressType != ConfigData->SwAddressType) ||
+ (OldConfigData->SwAddressLength != ConfigData->SwAddressLength) ||
+ (NetCompareMem (
+ OldConfigData->StationAddress,
+ ConfigData->StationAddress,
+ OldConfigData->SwAddressLength
+ ) != 0)) {
+ //
+ // Deny the unallowed changes.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+ } else {
+ //
+ // The instance is not configured.
+ //
+
+ if (ConfigData->SwAddressType == IPv4_ETHER_PROTO_TYPE) {
+ NetCopyMem (&Ip, ConfigData->StationAddress, sizeof (IP4_ADDR));
+
+ if (!Ip4IsUnicast (NTOHL (Ip), 0)) {
+ //
+ // The station address is not a valid IPv4 unicast address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Save the configuration.
+ //
+ CopyMem (OldConfigData, ConfigData, sizeof (EFI_ARP_CONFIG_DATA));
+
+ OldConfigData->StationAddress = NetAllocatePool (OldConfigData->SwAddressLength);
+ if (OldConfigData->StationAddress == NULL) {
+ ARP_DEBUG_ERROR (("ArpConfigInstance: NetAllocatePool for the StationAddress "
+ "failed.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Save the StationAddress.
+ //
+ NetCopyMem (
+ OldConfigData->StationAddress,
+ ConfigData->StationAddress,
+ OldConfigData->SwAddressLength
+ );
+
+ //
+ // Set the state to configured.
+ //
+ Instance->Configured = TRUE;
+ }
+
+ //
+ // Use the implementation specific values if the following field is zero.
+ //
+ OldConfigData->EntryTimeOut = (ConfigData->EntryTimeOut == 0) ?
+ ARP_DEFAULT_TIMEOUT_VALUE : ConfigData->EntryTimeOut;
+
+ OldConfigData->RetryCount = (ConfigData->RetryCount == 0) ?
+ ARP_DEFAULT_RETRY_COUNT : ConfigData->RetryCount;
+
+ OldConfigData->RetryTimeOut = (ConfigData->RetryTimeOut == 0) ?
+ ARP_DEFAULT_RETRY_INTERVAL : ConfigData->RetryTimeOut;
+ } else {
+ //
+ // Reset the configuration.
+ //
+
+ if (Instance->Configured) {
+ //
+ // Cancel the arp requests issued by this instance.
+ //
+ ArpCancelRequest (Instance, NULL, NULL);
+
+ //
+ // Free the buffer previously allocated to hold the station address.
+ //
+ NetFreePool (OldConfigData->StationAddress);
+ }
+
+ Instance->Configured = FALSE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Send out an arp frame using the CachEntry and the ArpOpCode.
+
+ @param Instance Pointer to the instance context data.
+ @param CacheEntry Pointer to the configuration data used to
+ configure the instance.
+ @param ArpOpCode The opcode used to send out this Arp frame, either
+ request or reply.
+
+ @return None.
+
+**/
+VOID
+ArpSendFrame (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN UINT16 ArpOpCode
+ )
+{
+ EFI_STATUS Status;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TxToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
+ UINT32 TotalLength;
+ UINT8 *Packet;
+ ARP_SERVICE_DATA *ArpService;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_ARP_CONFIG_DATA *ConfigData;
+ UINT8 *TmpPtr;
+ ARP_HEAD *ArpHead;
+
+ ASSERT ((Instance != NULL) && (CacheEntry != NULL));
+
+ //
+ // Allocate memory for the TxToken.
+ //
+ TxToken = NetAllocatePool (sizeof(EFI_MANAGED_NETWORK_COMPLETION_TOKEN));
+ if (TxToken == NULL) {
+ ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for TxToken failed.\n"));
+ return;
+ }
+
+ TxToken->Event = NULL;
+ TxData = NULL;
+ Packet = NULL;
+
+ //
+ // Create the event for this TxToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ NET_TPL_EVENT,
+ ArpOnFrameSent,
+ (VOID *)TxToken,
+ &TxToken->Event
+ );
+ if (EFI_ERROR (Status)) {
+ ARP_DEBUG_ERROR (("ArpSendFrame: CreateEvent failed for TxToken->Event.\n"));
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Allocate memory for the TxData used in the TxToken.
+ //
+ TxData = NetAllocatePool (sizeof(EFI_MANAGED_NETWORK_TRANSMIT_DATA));
+ if (TxData == NULL) {
+ ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for TxData failed.\n"));
+ goto CLEAN_EXIT;
+ }
+
+ ArpService = Instance->ArpService;
+ SnpMode = &ArpService->SnpMode;
+ ConfigData = &Instance->ConfigData;
+
+ //
+ // Calculate the buffer length for this arp frame.
+ //
+ TotalLength = SnpMode->MediaHeaderSize + sizeof (ARP_HEAD) +
+ 2 * (ConfigData->SwAddressLength + SnpMode->HwAddressSize);
+
+ //
+ // Allocate buffer for the arp frame.
+ //
+ Packet = NetAllocatePool (TotalLength);
+ if (Packet == NULL) {
+ ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for Packet failed.\n"));
+ }
+
+ TmpPtr = Packet;
+
+ //
+ // The destination MAC address.
+ //
+ if (ArpOpCode == ARP_OPCODE_REQUEST) {
+ NetCopyMem (TmpPtr, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize);
+ } else {
+ NetCopyMem (
+ TmpPtr,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ SnpMode->HwAddressSize
+ );
+ }
+ TmpPtr += SnpMode->HwAddressSize;
+
+ //
+ // The source MAC address.
+ //
+ NetCopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize);
+ TmpPtr += SnpMode->HwAddressSize;
+
+ //
+ // The ethernet protocol type.
+ //
+ *(UINT16 *)TmpPtr = HTONS (ARP_ETHER_PROTO_TYPE);
+ TmpPtr += 2;
+
+ //
+ // The ARP Head.
+ //
+ ArpHead = (ARP_HEAD *) TmpPtr;
+ ArpHead->HwType = HTONS ((UINT16)SnpMode->IfType);
+ ArpHead->ProtoType = HTONS (ConfigData->SwAddressType);
+ ArpHead->HwAddrLen = (UINT8)SnpMode->HwAddressSize;
+ ArpHead->ProtoAddrLen = ConfigData->SwAddressLength;
+ ArpHead->OpCode = HTONS (ArpOpCode);
+ TmpPtr += sizeof (ARP_HEAD);
+
+ //
+ // The sender hardware address.
+ //
+ NetCopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize);
+ TmpPtr += SnpMode->HwAddressSize;
+
+ //
+ // The sender protocol address.
+ //
+ NetCopyMem (TmpPtr, ConfigData->StationAddress, ConfigData->SwAddressLength);
+ TmpPtr += ConfigData->SwAddressLength;
+
+ //
+ // The target hardware address.
+ //
+ NetCopyMem (
+ TmpPtr,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ SnpMode->HwAddressSize
+ );
+ TmpPtr += SnpMode->HwAddressSize;
+
+ //
+ // The target protocol address.
+ //
+ NetCopyMem (
+ TmpPtr,
+ CacheEntry->Addresses[Protocol].AddressPtr,
+ ConfigData->SwAddressLength
+ );
+
+ //
+ // Set all the fields of the TxData.
+ //
+ TxData->DestinationAddress = NULL;
+ TxData->SourceAddress = NULL;
+ TxData->ProtocolType = 0;
+ TxData->DataLength = TotalLength - SnpMode->MediaHeaderSize;
+ TxData->HeaderLength = (UINT16) SnpMode->MediaHeaderSize;
+ TxData->FragmentCount = 1;
+
+ TxData->FragmentTable[0].FragmentBuffer = Packet;
+ TxData->FragmentTable[0].FragmentLength = TotalLength;
+
+ //
+ // Associate the TxData with the TxToken.
+ //
+ TxToken->Packet.TxData = TxData;
+ TxToken->Status = EFI_NOT_READY;
+
+ //
+ // Send out this arp packet by Mnp.
+ //
+ Status = ArpService->Mnp->Transmit (ArpService->Mnp, TxToken);
+ if (EFI_ERROR (Status)) {
+ ARP_DEBUG_ERROR (("Mnp->Transmit failed, %r.\n", Status));
+ goto CLEAN_EXIT;
+ }
+
+ return;
+
+CLEAN_EXIT:
+
+ if (Packet != NULL) {
+ NetFreePool (Packet);
+ }
+
+ if (TxData != NULL) {
+ NetFreePool (TxData);
+ }
+
+ if (TxToken->Event != NULL) {
+ gBS->CloseEvent (TxToken->Event);
+ }
+
+ NetFreePool (TxToken);
+}
+
+
+/**
+ Delete the cache entries in the specified CacheTable, using the BySwAddress,
+ SwAddressType, AddressBuffer combination as the matching key, if Force is TRUE,
+ the cache is deleted event it's a static entry.
+
+ @param CacheTable Pointer to the cache table to do the deletion.
+ @param BySwAddress Delete the cache entry by software address or by
+ hardware address.
+ @param SwAddressType The software address used to do the deletion.
+ @param AddressBuffer Pointer to the buffer containing the address to
+ match for the deletion.
+ @param Force This deletion is forced or not.
+
+ @return The count of the deleted cache entries.
+
+**/
+STATIC
+UINTN
+ArpDeleteCacheEntryInTable (
+ IN NET_LIST_ENTRY *CacheTable,
+ IN BOOLEAN BySwAddress,
+ IN UINT16 SwAddressType,
+ IN UINT8 *AddressBuffer OPTIONAL,
+ IN BOOLEAN Force
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *NextEntry;
+ ARP_CACHE_ENTRY *CacheEntry;
+ UINTN Count;
+
+ Count = 0;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, CacheTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+
+ if ((CacheEntry->DefaultDecayTime == 0) && !Force) {
+ //
+ // It's a static entry and we are not forced to delete it, skip.
+ //
+ continue;
+ }
+
+ if (BySwAddress) {
+ if (SwAddressType == CacheEntry->Addresses[Protocol].Type) {
+ //
+ // Protocol address type matched. Check the address.
+ //
+ if ((AddressBuffer == NULL) ||
+ (NetCompareMem (
+ AddressBuffer,
+ CacheEntry->Addresses[Protocol].AddressPtr,
+ CacheEntry->Addresses[Protocol].Length
+ ) == 0)) {
+ //
+ // Address matched.
+ //
+ goto MATCHED;
+ }
+ }
+ } else {
+ if ((AddressBuffer == NULL) ||
+ (NetCompareMem (
+ AddressBuffer,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ CacheEntry->Addresses[Hardware].Length
+ ) == 0)) {
+ //
+ // Address matched.
+ //
+ goto MATCHED;
+ }
+ }
+
+ continue;
+
+MATCHED:
+
+ //
+ // Delete this entry.
+ //
+ NetListRemoveEntry (&CacheEntry->List);
+ ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList));
+ NetFreePool (CacheEntry);
+
+ Count++;
+ }
+
+ return Count;
+}
+
+
+/**
+ Delete cache entries in all the cache tables.
+
+ @param Instance Pointer to the instance context data.
+ @param BySwAddress Delete the cache entry by software address or by
+ hardware address.
+ @param AddressBuffer Pointer to the buffer containing the address to
+ match for the deletion.
+ @param Force This deletion is forced or not.
+
+ @return The count of the deleted cache entries.
+
+**/
+UINTN
+ArpDeleteCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN BOOLEAN BySwAddress,
+ IN UINT8 *AddressBuffer OPTIONAL,
+ IN BOOLEAN Force
+ )
+{
+ ARP_SERVICE_DATA *ArpService;
+ UINTN Count;
+
+ NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);
+
+ ArpService = Instance->ArpService;
+
+ //
+ // Delete the cache entries in the DeniedCacheTable.
+ //
+ Count = ArpDeleteCacheEntryInTable (
+ &ArpService->DeniedCacheTable,
+ BySwAddress,
+ Instance->ConfigData.SwAddressType,
+ AddressBuffer,
+ Force
+ );
+
+ //
+ // Delete the cache entries inthe ResolvedCacheTable.
+ //
+ Count += ArpDeleteCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ BySwAddress,
+ Instance->ConfigData.SwAddressType,
+ AddressBuffer,
+ Force
+ );
+
+ return Count;
+}
+
+
+/**
+ Cancel the arp request.
+
+ @param Instance Pointer to the instance context data.
+ @param TargetSwAddress Pointer to the buffer containing the target
+ software address to match the arp request.
+ @param UserEvent The user event used to notify this request
+ cancellation.
+
+ @return The count of the cancelled requests.
+
+**/
+UINTN
+ArpCancelRequest (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT UserEvent OPTIONAL
+ )
+{
+ ARP_SERVICE_DATA *ArpService;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *NextEntry;
+ ARP_CACHE_ENTRY *CacheEntry;
+ UINTN Count;
+
+ NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);
+
+ ArpService = Instance->ArpService;
+
+ Count = 0;
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) {
+ CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);
+
+ if ((TargetSwAddress == NULL) ||
+ (NetCompareMem (
+ TargetSwAddress,
+ CacheEntry->Addresses[Protocol].AddressPtr,
+ CacheEntry->Addresses[Protocol].Length
+ ) == 0)) {
+ //
+ // This request entry matches the TargetSwAddress or all requests are to be
+ // cancelled as TargetSwAddress is NULL.
+ //
+ Count += ArpAddressResolved (CacheEntry, Instance, UserEvent);
+
+ if (NetListIsEmpty (&CacheEntry->UserRequestList)) {
+ //
+ // No user requests any more, remove this request cache entry.
+ //
+ NetListRemoveEntry (&CacheEntry->List);
+ NetFreePool (CacheEntry);
+ }
+ }
+ }
+
+ return Count;
+}
+
+
+/**
+ Find the cache entry in the cache table.
+
+ @param Instance Pointer to the instance context data.
+ @param BySwAddress Set to TRUE to look for matching software protocol
+ addresses. Set to FALSE to look for matching
+ hardware protocol addresses.
+ @param AddressBuffer Pointer to address buffer. Set to NULL to match
+ all addresses.
+ @param EntryLength The size of an entry in the entries buffer.
+ @param EntryCount The number of ARP cache entries that are found by
+ the specified criteria.
+ @param Entries Pointer to the buffer that will receive the ARP
+ cache entries.
+ @param Refresh Set to TRUE to refresh the timeout value of the
+ matching ARP cache entry.
+
+ @retval EFI_SUCCESS The requested ARP cache entries are copied into
+ the buffer.
+ @retval EFI_NOT_FOUND No matching entries found.
+ @retval EFI_OUT_OF_RESOURCE There is a memory allocation failure.
+
+**/
+EFI_STATUS
+ArpFindCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL,
+ OUT UINT32 *EntryLength OPTIONAL,
+ OUT UINT32 *EntryCount OPTIONAL,
+ OUT EFI_ARP_FIND_DATA **Entries OPTIONAL,
+ IN BOOLEAN Refresh
+ )
+{
+ EFI_STATUS Status;
+ ARP_SERVICE_DATA *ArpService;
+ NET_ARP_ADDRESS MatchAddress;
+ FIND_OPTYPE FindOpType;
+ NET_LIST_ENTRY *StartEntry;
+ ARP_CACHE_ENTRY *CacheEntry;
+ NET_MAP FoundEntries;
+ UINT32 FoundCount;
+ EFI_ARP_FIND_DATA *FindData;
+ NET_LIST_ENTRY *CacheTable;
+
+ ArpService = Instance->ArpService;
+
+ //
+ // Init the FounEntries used to hold the found cache entries.
+ //
+ NetMapInit (&FoundEntries);
+
+ //
+ // Set the MatchAddress.
+ //
+ if (BySwAddress) {
+ MatchAddress.Type = Instance->ConfigData.SwAddressType;
+ MatchAddress.Length = Instance->ConfigData.SwAddressLength;
+ FindOpType = ByProtoAddress;
+ } else {
+ MatchAddress.Type = ArpService->SnpMode.IfType;
+ MatchAddress.Length = (UINT8)ArpService->SnpMode.HwAddressSize;
+ FindOpType = ByHwAddress;
+ }
+
+ MatchAddress.AddressPtr = AddressBuffer;
+
+ //
+ // Search the DeniedCacheTable
+ //
+ StartEntry = NULL;
+ while (TRUE) {
+ //
+ // Try to find the matched entries in the DeniedCacheTable.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->DeniedCacheTable,
+ StartEntry,
+ FindOpType,
+ &MatchAddress,
+ &MatchAddress
+ );
+ if (CacheEntry == NULL) {
+ //
+ // Once the CacheEntry is NULL, there are no more matches.
+ //
+ break;
+ }
+
+ //
+ // Insert the found entry into the map.
+ //
+ NetMapInsertTail (
+ &FoundEntries,
+ (VOID *)CacheEntry,
+ (VOID *)&ArpService->DeniedCacheTable
+ );
+
+ //
+ // Let the next search start from this cache entry.
+ //
+ StartEntry = &CacheEntry->List;
+
+ if (Refresh) {
+ //
+ // Refresh the DecayTime if needed.
+ //
+ CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;
+ }
+ }
+
+ //
+ // Search the ResolvedCacheTable
+ //
+ StartEntry = NULL;
+ while (TRUE) {
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ StartEntry,
+ FindOpType,
+ &MatchAddress,
+ &MatchAddress
+ );
+ if (CacheEntry == NULL) {
+ //
+ // Once the CacheEntry is NULL, there are no more matches.
+ //
+ break;
+ }
+
+ //
+ // Insert the found entry into the map.
+ //
+ NetMapInsertTail (
+ &FoundEntries,
+ (VOID *)CacheEntry,
+ (VOID *)&ArpService->ResolvedCacheTable
+ );
+
+ //
+ // Let the next search start from this cache entry.
+ //
+ StartEntry = &CacheEntry->List;
+
+ if (Refresh) {
+ //
+ // Refresh the DecayTime if needed.
+ //
+ CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+ FoundCount = (UINT32) NetMapGetCount (&FoundEntries);
+ if (FoundCount == 0) {
+ Status = EFI_NOT_FOUND;
+ goto CLEAN_EXIT;
+ }
+
+ if (EntryLength != NULL) {
+ //
+ // Return the entry length.
+ //
+ *EntryLength = sizeof (EFI_ARP_FIND_DATA) + Instance->ConfigData.SwAddressLength +
+ ArpService->SnpMode.HwAddressSize;
+ }
+
+ if (EntryCount != NULL) {
+ //
+ // Return the found entry count.
+ //
+ *EntryCount = FoundCount;
+ }
+
+ if (Entries == NULL) {
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Allocate buffer to copy the found entries.
+ //
+ FindData = NetAllocatePool (FoundCount * (*EntryLength));
+ if (FindData == NULL) {
+ ARP_DEBUG_ERROR (("ArpFindCacheEntry: Failed to allocate memory.\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Return the address to the user.
+ //
+ *Entries = FindData;
+
+ //
+ // Dump the entries.
+ //
+ while (!NetMapIsEmpty (&FoundEntries)) {
+ //
+ // Get a cache entry from the map.
+ //
+ CacheEntry = NetMapRemoveHead (&FoundEntries, (VOID **)&CacheTable);
+
+ //
+ // Set the fields in FindData.
+ //
+ FindData->Size = *EntryLength;
+ FindData->DenyFlag = (BOOLEAN)(CacheTable == &ArpService->DeniedCacheTable);
+ FindData->StaticFlag = (BOOLEAN)(CacheEntry->DefaultDecayTime == 0);
+ FindData->HwAddressType = ArpService->SnpMode.IfType;
+ FindData->SwAddressType = Instance->ConfigData.SwAddressType;
+ FindData->HwAddressLength = (UINT8)ArpService->SnpMode.HwAddressSize;
+ FindData->SwAddressLength = Instance->ConfigData.SwAddressLength;
+
+ //
+ // Copy the software address.
+ //
+ NetCopyMem (
+ FindData + 1,
+ CacheEntry->Addresses[Protocol].AddressPtr,
+ FindData->SwAddressLength
+ );
+
+ //
+ // Copy the hardware address.
+ //
+ NetCopyMem (
+ (UINT8 *)(FindData + 1) + FindData->SwAddressLength,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ FindData->HwAddressLength
+ );
+
+ //
+ // Slip to the next FindData.
+ //
+ FindData = (EFI_ARP_FIND_DATA *)((UINT8 *)FindData + *EntryLength);
+ }
+
+CLEAN_EXIT:
+
+ NetMapClean (&FoundEntries);
+
+ return Status;
+}
+
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h
new file mode 100644
index 0000000000..607443a99e
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h
@@ -0,0 +1,341 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ArpImpl.h
+
+Abstract:
+
+
+**/
+
+#ifndef _ARP_IMPL_H_
+#define _ARP_IMPL_H_
+
+
+#include <PiDxe.h>
+
+#include <Protocol/Arp.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include "ArpDebug.h"
+
+#define ARP_ETHER_PROTO_TYPE 0x0806
+#define IPv4_ETHER_PROTO_TYPE 0x0800
+#define IPv6_ETHER_PROTO_TYPE 0x86DD
+
+#define ARP_OPCODE_REQUEST 0x0001
+#define ARP_OPCODE_REPLY 0x0002
+
+#define ARP_DEFAULT_TIMEOUT_VALUE (400 * TICKS_PER_SECOND)
+#define ARP_DEFAULT_RETRY_COUNT 2
+#define ARP_DEFAULT_RETRY_INTERVAL (5 * TICKS_PER_MS)
+#define ARP_PERIODIC_TIMER_INTERVAL (500 * TICKS_PER_MS)
+
+#pragma pack(1)
+typedef struct _ARP_HEAD {
+ UINT16 HwType;
+ UINT16 ProtoType;
+ UINT8 HwAddrLen;
+ UINT8 ProtoAddrLen;
+ UINT16 OpCode;
+} ARP_HEAD;
+#pragma pack()
+
+typedef struct _ARP_ADDRESS {
+ UINT8 *SenderHwAddr;
+ UINT8 *SenderProtoAddr;
+ UINT8 *TargetHwAddr;
+ UINT8 *TargetProtoAddr;
+} ARP_ADDRESS;
+
+#define MATCH_SW_ADDRESS 0x1
+#define MATCH_HW_ADDRESS 0x2
+
+typedef enum {
+ ByNone = 0,
+ ByProtoAddress = MATCH_SW_ADDRESS,
+ ByHwAddress = MATCH_HW_ADDRESS,
+ ByBoth = MATCH_SW_ADDRESS | MATCH_HW_ADDRESS
+} FIND_OPTYPE;
+
+#define ARP_INSTANCE_DATA_SIGNATURE EFI_SIGNATURE_32('A', 'R', 'P', 'I')
+
+#define ARP_INSTANCE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ ARP_INSTANCE_DATA, \
+ ArpProto, \
+ ARP_INSTANCE_DATA_SIGNATURE \
+ )
+
+typedef struct _ARP_SERVICE_DATA ARP_SERVICE_DATA;
+
+typedef struct _ARP_INSTANCE_DATA {
+ UINT32 Signature;
+ ARP_SERVICE_DATA *ArpService;
+ EFI_HANDLE Handle;
+ EFI_ARP_PROTOCOL ArpProto;
+ NET_LIST_ENTRY List;
+ EFI_ARP_CONFIG_DATA ConfigData;
+ BOOLEAN Configured;
+ BOOLEAN Destroyed;
+} ARP_INSTANCE_DATA;
+
+#define ARP_SERVICE_DATA_SIGNATURE EFI_SIGNATURE_32('A', 'R', 'P', 'S')
+
+#define ARP_SERVICE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ ARP_SERVICE_DATA, \
+ ServiceBinding, \
+ ARP_SERVICE_DATA_SIGNATURE \
+ )
+
+struct _ARP_SERVICE_DATA {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ EFI_HANDLE MnpChildHandle;
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE ControllerHandle;
+
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN RxToken;
+
+ EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+ NET_LOCK Lock;
+
+ UINTN ChildrenNumber;
+ NET_LIST_ENTRY ChildrenList;
+
+ NET_LIST_ENTRY PendingRequestTable;
+ NET_LIST_ENTRY DeniedCacheTable;
+ NET_LIST_ENTRY ResolvedCacheTable;
+
+ EFI_EVENT PeriodicTimer;
+};
+
+typedef struct _USER_REQUEST_CONTEXT {
+ NET_LIST_ENTRY List;
+ ARP_INSTANCE_DATA *Instance;
+ EFI_EVENT UserRequestEvent;
+ VOID *UserHwAddrBuffer;
+} USER_REQUEST_CONTEXT;
+
+#define ARP_MAX_PROTOCOL_ADDRESS_LEN sizeof(EFI_IP_ADDRESS)
+#define ARP_MAX_HARDWARE_ADDRESS_LEN sizeof(EFI_MAC_ADDRESS)
+
+typedef struct _NET_ARP_ADDRESS {
+ UINT16 Type;
+ UINT8 Length;
+ UINT8 *AddressPtr;
+ union {
+ UINT8 ProtoAddress[ARP_MAX_PROTOCOL_ADDRESS_LEN];
+ UINT8 HwAddress[ARP_MAX_HARDWARE_ADDRESS_LEN];
+ } Buffer;
+} NET_ARP_ADDRESS;
+
+typedef enum {
+ Hardware,
+ Protocol
+} ARP_ADDRESS_TYPE;
+
+typedef struct _ARP_CACHE_ENTRY {
+ NET_LIST_ENTRY List;
+
+ UINT32 RetryCount;
+ UINT32 DefaultDecayTime;
+ UINT32 DecayTime;
+ UINT32 NextRetryTime;
+
+ NET_ARP_ADDRESS Addresses[2];
+
+ NET_LIST_ENTRY UserRequestList;
+} ARP_CACHE_ENTRY;
+
+EFI_STATUS
+EFIAPI
+ArpConfigure (
+ IN EFI_ARP_PROTOCOL *This,
+ IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+ArpAdd (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN DenyFlag,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN VOID *TargetHwAddress OPTIONAL,
+ IN UINT32 TimeoutValue,
+ IN BOOLEAN Overwrite
+ );
+
+EFI_STATUS
+EFIAPI
+ArpFind (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL,
+ OUT UINT32 *EntryLength OPTIONAL,
+ OUT UINT32 *EntryCount OPTIONAL,
+ OUT EFI_ARP_FIND_DATA **Entries OPTIONAL,
+ IN BOOLEAN Refresh
+ );
+
+EFI_STATUS
+EFIAPI
+ArpDelete (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+ArpFlush (
+ IN EFI_ARP_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+ArpRequest (
+ IN EFI_ARP_PROTOCOL *This,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT ResolvedEvent OPTIONAL,
+ OUT VOID *TargetHwAddress
+ );
+
+EFI_STATUS
+EFIAPI
+ArpCancel (
+ IN EFI_ARP_PROTOCOL *This,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT ResolvedEvent OPTIONAL
+ );
+
+EFI_STATUS
+ArpConfigureInstance (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL
+ );
+
+ARP_CACHE_ENTRY *
+ArpFindDeniedCacheEntry (
+ IN ARP_SERVICE_DATA *ArpService,
+ IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL,
+ IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL
+ );
+
+ARP_CACHE_ENTRY *
+ArpFindNextCacheEntryInTable (
+ IN NET_LIST_ENTRY *CacheTable,
+ IN NET_LIST_ENTRY *StartEntry,
+ IN FIND_OPTYPE FindOpType,
+ IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL,
+ IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL
+ );
+
+ARP_CACHE_ENTRY *
+ArpAllocCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance
+ );
+
+VOID
+ArpFillAddressInCacheEntry (
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN NET_ARP_ADDRESS *HwAddr OPTIONAL,
+ IN NET_ARP_ADDRESS *SwAddr OPTIONAL
+ );
+
+UINTN
+ArpAddressResolved (
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN ARP_INSTANCE_DATA *Instance OPTIONAL,
+ IN EFI_EVENT UserEvent OPTIONAL
+ );
+
+UINTN
+ArpDeleteCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN BOOLEAN BySwAddress,
+ IN UINT8 *AddressBuffer OPTIONAL,
+ IN BOOLEAN Force
+ );
+
+VOID
+ArpSendFrame (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN ARP_CACHE_ENTRY *CacheEntry,
+ IN UINT16 ArpOpCode
+ );
+
+VOID
+ArpInitInstance (
+ IN ARP_SERVICE_DATA *ArpService,
+ IN ARP_INSTANCE_DATA *Instance
+ );
+
+VOID
+EFIAPI
+ArpOnFrameRcvd (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+VOID
+EFIAPI
+ArpOnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+VOID
+EFIAPI
+ArpTimerHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+UINTN
+ArpCancelRequest (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT UserEvent OPTIONAL
+ );
+
+EFI_STATUS
+ArpFindCacheEntry (
+ IN ARP_INSTANCE_DATA *Instance,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL,
+ OUT UINT32 *EntryLength OPTIONAL,
+ OUT UINT32 *EntryCount OPTIONAL,
+ OUT EFI_ARP_FIND_DATA **Entries OPTIONAL,
+ IN BOOLEAN Refresh
+ );
+
+#endif
+
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c b/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c
new file mode 100644
index 0000000000..eb1b082a21
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c
@@ -0,0 +1,727 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ArpMain.c
+
+Abstract:
+
+
+**/
+
+#include "ArpImpl.h"
+
+
+/**
+ This function is used to assign a station address to the ARP cache for this instance
+ of the ARP driver. A call to this function with the ConfigData field set to NULL
+ will reset this ARP instance.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param ConfigData Pointer to the EFI_ARP_CONFIG_DATA structure.
+
+ @retval EFI_SUCCESS The new station address was successfully
+ registered.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. SwAddressLength is zero when
+ ConfigData is not NULL. StationAddress is NULL
+ when ConfigData is not NULL.
+ @retval EFI_ACCESS_DENIED The SwAddressType, SwAddressLength, or
+ StationAddress is different from the one that is
+ already registered.
+ @retval EFI_OUT_OF_RESOURCES Storage for the new StationAddress could not be
+ allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpConfigure (
+ IN EFI_ARP_PROTOCOL *This,
+ IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ ARP_INSTANCE_DATA *Instance;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((ConfigData != NULL) &&
+ ((ConfigData->SwAddressLength == 0) ||
+ (ConfigData->StationAddress == NULL) ||
+ (ConfigData->SwAddressType <= 1500))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (EFI_ERROR (NET_TRYLOCK (&Instance->ArpService->Lock))) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Configure this instance, the ConfigData has already passed the basic checks.
+ //
+ Status = ArpConfigureInstance (Instance, ConfigData);
+
+ NET_UNLOCK (&Instance->ArpService->Lock);
+
+ return Status;
+}
+
+
+/**
+ This function is used to insert entries into the ARP cache.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param DenyFlag Set to TRUE if this entry is a deny entry. Set to
+ FALSE if this entry is a normal entry.
+ @param TargetSwAddress Pointer to a protocol address to add (or deny).
+ May be set to NULL if DenyFlag is TRUE.
+ @param TargetHwAddress Pointer to a hardware address to add (or deny).
+ May be set to NULL if DenyFlag is TRUE.
+ @param TimeoutValue Time in 100-ns units that this entry will remain
+ in the ARP cache. A value of zero means that the
+ entry is permanent. A nonzero value will override
+ the one given by Configure() if the entry to be
+ added is a dynamic entry.
+ @param Overwrite If TRUE, the matching cache entry will be
+ overwritten with the supplied parameters. If
+ FALSE, EFI_ACCESS_DENIED is returned if the
+ corresponding cache entry already exists.
+
+ @retval EFI_SUCCESS The entry has been added or updated.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. DenyFlag is FALSE and
+ TargetHwAddress is NULL. DenyFlag is FALSE and
+ TargetSwAddress is NULL. TargetHwAddress is NULL
+ and TargetSwAddress is NULL. Both TargetSwAddress
+ and TargetHwAddress are not NULL when DenyFlag is
+ TRUE.
+ @retval EFI_OUT_OF_RESOURCES The new ARP cache entry could not be allocated.
+ @retval EFI_ACCESS_DENIED The ARP cache entry already exists and Overwrite
+ is not true.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpAdd (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN DenyFlag,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN VOID *TargetHwAddress OPTIONAL,
+ IN UINT32 TimeoutValue,
+ IN BOOLEAN Overwrite
+ )
+{
+ EFI_STATUS Status;
+ ARP_INSTANCE_DATA *Instance;
+ ARP_SERVICE_DATA *ArpService;
+ ARP_CACHE_ENTRY *CacheEntry;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ NET_ARP_ADDRESS MatchAddress[2];
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((!DenyFlag) && ((TargetHwAddress == NULL) || (TargetSwAddress == NULL))) ||
+ (DenyFlag && (TargetHwAddress != NULL) && (TargetSwAddress != NULL)) ||
+ ((TargetHwAddress == NULL) && (TargetSwAddress == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Status = EFI_SUCCESS;
+ ArpService = Instance->ArpService;
+ SnpMode = &Instance->ArpService->SnpMode;
+
+ //
+ // Fill the hardware address part in the MatchAddress.
+ //
+ MatchAddress[Hardware].Type = SnpMode->IfType;
+ MatchAddress[Hardware].Length = (UINT8) SnpMode->HwAddressSize;
+ MatchAddress[Hardware].AddressPtr = TargetHwAddress;
+
+ //
+ // Fill the software address part in the MatchAddress.
+ //
+ MatchAddress[Protocol].Type = Instance->ConfigData.SwAddressType;
+ MatchAddress[Protocol].Length = Instance->ConfigData.SwAddressLength;
+ MatchAddress[Protocol].AddressPtr = TargetSwAddress;
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // See whether the entry to add exists. Check the DeinedCacheTable first.
+ //
+ CacheEntry = ArpFindDeniedCacheEntry (
+ ArpService,
+ &MatchAddress[Protocol],
+ &MatchAddress[Hardware]
+ );
+
+ if (CacheEntry == NULL) {
+ //
+ // Check the ResolvedCacheTable
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ NULL,
+ ByBoth,
+ &MatchAddress[Protocol],
+ &MatchAddress[Hardware]
+ );
+ }
+
+ if ((CacheEntry != NULL) && !Overwrite) {
+ //
+ // The entry to add exists, if not Overwirte, deny this add request.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto UNLOCK_EXIT;
+ }
+
+ if ((CacheEntry == NULL) && (TargetSwAddress != NULL)) {
+ //
+ // Check whether there are pending requests matching the entry to be added.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->PendingRequestTable,
+ NULL,
+ ByProtoAddress,
+ &MatchAddress[Protocol],
+ NULL
+ );
+ }
+
+ if (CacheEntry != NULL) {
+ //
+ // Remove it from the Table.
+ //
+ NetListRemoveEntry (&CacheEntry->List);
+ } else {
+ //
+ // It's a new entry, allocate memory for the entry.
+ //
+ CacheEntry = ArpAllocCacheEntry (Instance);
+
+ if (CacheEntry == NULL) {
+ ARP_DEBUG_ERROR (("ArpAdd: Failed to allocate pool for CacheEntry.\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UNLOCK_EXIT;
+ }
+ }
+
+ //
+ // Overwrite these parameters.
+ //
+ CacheEntry->DefaultDecayTime = TimeoutValue;
+ CacheEntry->DecayTime = TimeoutValue;
+
+ //
+ // Fill in the addresses.
+ //
+ ArpFillAddressInCacheEntry (
+ CacheEntry,
+ &MatchAddress[Hardware],
+ &MatchAddress[Protocol]
+ );
+
+ //
+ // Inform the user if there is any.
+ //
+ ArpAddressResolved (CacheEntry, NULL, NULL);
+
+ //
+ // Add this CacheEntry to the corresponding CacheTable.
+ //
+ if (DenyFlag) {
+ NetListInsertHead (&ArpService->DeniedCacheTable, &CacheEntry->List);
+ } else {
+ NetListInsertHead (&ArpService->ResolvedCacheTable, &CacheEntry->List);
+ }
+
+UNLOCK_EXIT:
+
+ NET_UNLOCK (&ArpService->Lock);
+
+ return Status;
+}
+
+
+/**
+ This function searches the ARP cache for matching entries and allocates a buffer into
+ which those entries are copied.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param BySwAddress Set to TRUE to look for matching software protocol
+ addresses. Set to FALSE to look for matching
+ hardware protocol addresses.
+ @param AddressBuffer Pointer to address buffer. Set to NULL to match
+ all addresses.
+ @param EntryLength The size of an entry in the entries buffer.
+ @param EntryCount The number of ARP cache entries that are found by
+ the specified criteria.
+ @param Entries Pointer to the buffer that will receive the ARP
+ cache entries.
+ @param Refresh Set to TRUE to refresh the timeout value of the
+ matching ARP cache entry.
+
+ @retval EFI_SUCCESS The requested ARP cache entries were copied into
+ the buffer.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. Both EntryCount and EntryLength are
+ NULL, when Refresh is FALSE.
+ @retval EFI_NOT_FOUND No matching entries were found.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpFind (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL,
+ OUT UINT32 *EntryLength OPTIONAL,
+ OUT UINT32 *EntryCount OPTIONAL,
+ OUT EFI_ARP_FIND_DATA **Entries OPTIONAL,
+ IN BOOLEAN Refresh
+ )
+{
+ EFI_STATUS Status;
+ ARP_INSTANCE_DATA *Instance;
+ ARP_SERVICE_DATA *ArpService;
+
+ if ((This == NULL) ||
+ (!Refresh && (EntryCount == NULL) && (EntryLength == NULL)) ||
+ ((Entries != NULL) && ((EntryLength == NULL) || (EntryCount == NULL)))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+ ArpService = Instance->ArpService;
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // All the check passed, find the cache entries now.
+ //
+ Status = ArpFindCacheEntry (
+ Instance,
+ BySwAddress,
+ AddressBuffer,
+ EntryLength,
+ EntryCount,
+ Entries,
+ Refresh
+ );
+
+ NET_UNLOCK (&ArpService->Lock);
+
+ return Status;
+}
+
+
+/**
+ This function removes specified ARP cache entries.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param BySwAddress Set to TRUE to delete matching protocol addresses.
+ Set to FALSE to delete matching hardware
+ addresses.
+ @param AddressBuffer Pointer to the address buffer that is used as a
+ key to look for the cache entry. Set to NULL to
+ delete all entries.
+
+ @retval EFI_SUCCESS The entry was removed from the ARP cache.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND The specified deletion key was not found.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpDelete (
+ IN EFI_ARP_PROTOCOL *This,
+ IN BOOLEAN BySwAddress,
+ IN VOID *AddressBuffer OPTIONAL
+ )
+{
+ ARP_INSTANCE_DATA *Instance;
+ ARP_SERVICE_DATA *ArpService;
+ UINTN Count;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ ArpService = Instance->ArpService;
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Delete the specified cache entries.
+ //
+ Count = ArpDeleteCacheEntry (Instance, BySwAddress, AddressBuffer, TRUE);
+
+ NET_UNLOCK (&ArpService->Lock);
+
+ return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;
+}
+
+
+/**
+ This function delete all dynamic entries from the ARP cache that match the specified
+ software protocol type.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The cache has been flushed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND There are no matching dynamic cache entries.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpFlush (
+ IN EFI_ARP_PROTOCOL *This
+ )
+{
+ ARP_INSTANCE_DATA *Instance;
+ ARP_SERVICE_DATA *ArpService;
+ UINTN Count;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ ArpService = Instance->ArpService;
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Delete the dynamic entries from the cache table.
+ //
+ Count = ArpDeleteCacheEntry (Instance, FALSE, NULL, FALSE);
+
+ NET_UNLOCK (&ArpService->Lock);
+
+ return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;
+}
+
+
+/**
+ This function tries to resolve the TargetSwAddress and optionally returns a
+ TargetHwAddress if it already exists in the ARP cache.
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param TargetSwAddress Pointer to the protocol address to resolve.
+ @param ResolvedEvent Pointer to the event that will be signaled when
+ the address is resolved or some error occurs.
+ @param TargetHwAddress Pointer to the buffer for the resolved hardware
+ address in network byte order.
+
+ @retval EFI_SUCCESS The data is copied from the ARP cache into the
+ TargetHwAddress buffer.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. TargetHwAddress is NULL.
+ @retval EFI_ACCESS_DENIED The requested address is not present in the normal
+ ARP cache but is present in the deny address list.
+ Outgoing traffic to that address is forbidden.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+ @retval EFI_NOT_READY The request has been started and is not finished.
+
+**/
+EFI_STATUS
+EFIAPI
+ArpRequest (
+ IN EFI_ARP_PROTOCOL *This,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT ResolvedEvent OPTIONAL,
+ OUT VOID *TargetHwAddress
+ )
+{
+ EFI_STATUS Status;
+ ARP_INSTANCE_DATA *Instance;
+ ARP_SERVICE_DATA *ArpService;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ ARP_CACHE_ENTRY *CacheEntry;
+ NET_ARP_ADDRESS HardwareAddress;
+ NET_ARP_ADDRESS ProtocolAddress;
+ USER_REQUEST_CONTEXT *RequestContext;
+
+ if ((This == NULL) || (TargetHwAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Status = EFI_SUCCESS;
+ ArpService = Instance->ArpService;
+ SnpMode = &ArpService->SnpMode;
+
+ if ((TargetSwAddress == NULL) ||
+ ((Instance->ConfigData.SwAddressType == IPv4_ETHER_PROTO_TYPE) &&
+ IP4_IS_LOCAL_BROADCAST (*((UINT32 *)TargetSwAddress)))) {
+ //
+ // Return the hardware broadcast address.
+ //
+ NetCopyMem (TargetHwAddress, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize);
+
+ goto SIGNAL_USER;
+ }
+
+ if ((Instance->ConfigData.SwAddressType == IPv4_ETHER_PROTO_TYPE) &&
+ IP4_IS_MULTICAST (NTOHL (*((UINT32 *)TargetSwAddress)))) {
+ //
+ // If the software address is an IPv4 multicast address, invoke Mnp to
+ // resolve the address.
+ //
+ Status = ArpService->Mnp->McastIpToMac (
+ ArpService->Mnp,
+ FALSE,
+ TargetSwAddress,
+ TargetHwAddress
+ );
+ goto SIGNAL_USER;
+ }
+
+ HardwareAddress.Type = SnpMode->IfType;
+ HardwareAddress.Length = (UINT8)SnpMode->HwAddressSize;
+ HardwareAddress.AddressPtr = NULL;
+
+ ProtocolAddress.Type = Instance->ConfigData.SwAddressType;
+ ProtocolAddress.Length = Instance->ConfigData.SwAddressLength;
+ ProtocolAddress.AddressPtr = TargetSwAddress;
+
+ //
+ // Initialize the TargetHwAddrss to a zero address.
+ //
+ NetZeroMem (TargetHwAddress, SnpMode->HwAddressSize);
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Check whether the software address is in the denied table.
+ //
+ CacheEntry = ArpFindDeniedCacheEntry (ArpService, &ProtocolAddress, NULL);
+ if (CacheEntry != NULL) {
+ Status = EFI_ACCESS_DENIED;
+ goto UNLOCK_EXIT;
+ }
+
+ //
+ // Check whether the software address is already resolved.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->ResolvedCacheTable,
+ NULL,
+ ByProtoAddress,
+ &ProtocolAddress,
+ NULL
+ );
+ if (CacheEntry != NULL) {
+ //
+ // Resolved, copy the address into the user buffer.
+ //
+ NetCopyMem (
+ TargetHwAddress,
+ CacheEntry->Addresses[Hardware].AddressPtr,
+ CacheEntry->Addresses[Hardware].Length
+ );
+
+ goto UNLOCK_EXIT;
+ }
+
+ if (ResolvedEvent == NULL) {
+ Status = EFI_NOT_READY;
+ goto UNLOCK_EXIT;
+ }
+
+ //
+ // Create a request context for this arp request.
+ //
+ RequestContext = NetAllocatePool (sizeof(USER_REQUEST_CONTEXT));
+ if (RequestContext == NULL) {
+ ARP_DEBUG_ERROR (("ArpRequest: Allocate memory for RequestContext failed.\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UNLOCK_EXIT;
+ }
+
+ RequestContext->Instance = Instance;
+ RequestContext->UserRequestEvent = ResolvedEvent;
+ RequestContext->UserHwAddrBuffer = TargetHwAddress;
+ NetListInit (&RequestContext->List);
+
+ //
+ // Check whether there is a same request.
+ //
+ CacheEntry = ArpFindNextCacheEntryInTable (
+ &ArpService->PendingRequestTable,
+ NULL,
+ ByProtoAddress,
+ &ProtocolAddress,
+ NULL
+ );
+ if (CacheEntry != NULL) {
+
+ CacheEntry->NextRetryTime = Instance->ConfigData.RetryTimeOut;
+ CacheEntry->RetryCount = Instance->ConfigData.RetryCount;
+ } else {
+ //
+ // Allocate a cache entry for this request.
+ //
+ CacheEntry = ArpAllocCacheEntry (Instance);
+ if (CacheEntry == NULL) {
+ ARP_DEBUG_ERROR (("ArpRequest: Allocate memory for CacheEntry failed.\n"));
+ NetFreePool (RequestContext);
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UNLOCK_EXIT;
+ }
+
+ //
+ // Fill the software address.
+ //
+ ArpFillAddressInCacheEntry (CacheEntry, &HardwareAddress, &ProtocolAddress);
+
+ //
+ // Add this entry into the PendingRequestTable.
+ //
+ NetListInsertTail (&ArpService->PendingRequestTable, &CacheEntry->List);
+ }
+
+ //
+ // Link this request context into the cache entry.
+ //
+ NetListInsertHead (&CacheEntry->UserRequestList, &RequestContext->List);
+
+ //
+ // Send out the ARP Request frame.
+ //
+ ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REQUEST);
+ Status = EFI_NOT_READY;
+
+UNLOCK_EXIT:
+
+ NET_UNLOCK (&ArpService->Lock);
+
+SIGNAL_USER:
+
+ if ((ResolvedEvent != NULL) && (Status == EFI_SUCCESS)) {
+ gBS->SignalEvent (ResolvedEvent);
+ }
+
+ return Status;
+}
+
+
+/**
+ This function aborts the previous ARP request (identified by This, TargetSwAddress
+ and ResolvedEvent) that is issued by EFI_ARP_PROTOCOL.Request().
+
+ @param This Pointer to the EFI_ARP_PROTOCOL instance.
+ @param TargetSwAddress Pointer to the protocol address in previous
+ request session.
+ @param ResolvedEvent Pointer to the event that is used as the
+ notification event in previous request session.
+
+ @retval EFI_SUCCESS The pending request session(s) is/are aborted and
+ corresponding event(s) is/are signaled.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. TargetSwAddress is not NULL and
+ ResolvedEvent is NULL. TargetSwAddress is NULL and
+ ResolvedEvent is not NULL.
+ @retval EFI_NOT_STARTED The ARP driver instance has not been configured.
+ @retval EFI_NOT_FOUND The request is not issued by
+ EFI_ARP_PROTOCOL.Request().
+
+**/
+EFI_STATUS
+EFIAPI
+ArpCancel (
+ IN EFI_ARP_PROTOCOL *This,
+ IN VOID *TargetSwAddress OPTIONAL,
+ IN EFI_EVENT ResolvedEvent OPTIONAL
+ )
+{
+ ARP_INSTANCE_DATA *Instance;
+ ARP_SERVICE_DATA *ArpService;
+ UINTN Count;
+
+ if ((This == NULL) ||
+ ((TargetSwAddress != NULL) && (ResolvedEvent == NULL)) ||
+ ((TargetSwAddress == NULL) && (ResolvedEvent != NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = ARP_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ ArpService = Instance->ArpService;
+
+ if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Cancel the specified request.
+ //
+ Count = ArpCancelRequest (Instance, TargetSwAddress, ResolvedEvent);
+
+ NET_UNLOCK (&ArpService->Lock);
+
+ return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c b/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c
new file mode 100644
index 0000000000..ad92f3cc31
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c
@@ -0,0 +1,156 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+#include "ArpDriver.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+ArpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+ArpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gArpComponentName = {
+ ArpComponentNameGetDriverName,
+ ArpComponentNameGetControllerName,
+ "eng"
+};
+
+STATIC EFI_UNICODE_STRING_TABLE mArpDriverNameTable[] = {
+ { "eng", L"ARP Network Service Driver" },
+ { NULL, NULL }
+};
+
+EFI_STATUS
+EFIAPI
+ArpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCES - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gArpComponentName.SupportedLanguages,
+ mArpDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+ArpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
+
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..6808e771ac
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c
@@ -0,0 +1,162 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+DhcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+DhcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gDhcp4ComponentName = {
+ DhcpComponentNameGetDriverName,
+ DhcpComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mDhcpDriverNameTable[] = {
+ {
+ "eng",
+ L"DHCP Protocol Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+DhcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCES - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gDhcp4ComponentName.SupportedLanguages,
+ mDhcpDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+DhcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language
+ specified by Language from the point of view of the
+ driver specified by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle isn't NULL and isn't a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c
new file mode 100644
index 0000000000..08d6076bda
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c
@@ -0,0 +1,665 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Dhcp4Driver.c
+
+Abstract:
+
+
+**/
+
+#include "Dhcp4Impl.h"
+#include "Dhcp4Driver.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gDhcp4DriverBinding = {
+ Dhcp4DriverBindingSupported,
+ Dhcp4DriverBindingStart,
+ Dhcp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL mDhcp4ServiceBindingTemplete = {
+ Dhcp4ServiceBindingCreateChild,
+ Dhcp4ServiceBindingDestroyChild
+};
+
+//@MT: EFI_DRIVER_ENTRY_POINT (Dhcp4DriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+Dhcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ Entry point of the DHCP driver to install various protocols.
+
+Arguments:
+
+ ImageHandle - The driver's image handle
+ SystemTable - The system table
+
+Returns:
+
+ EFI_SUCCESS - All the related protocols are installed.
+ Others - Failed to install the protocols.
+
+--*/
+{
+ return NetLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gDhcp4DriverBinding,
+ ImageHandle,
+ &gDhcp4ComponentName,
+ NULL,
+ NULL
+ );
+}
+
+
+/**
+ Test to see if DHCP driver supports the ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver supports this device
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Configure the default UDP child to receive all the DHCP traffics
+ on this network interface.
+
+ @param UdpIo The UDP IO port to configure
+ @param Context The context to the function
+
+ @retval EFI_SUCCESS The UDP IO port is successfully configured.
+ @retval Others Failed to configure the UDP child.
+
+**/
+EFI_STATUS
+DhcpConfigUdpIo (
+ IN UDP_IO_PORT *UdpIo,
+ IN VOID *Context
+ )
+{
+ EFI_UDP4_CONFIG_DATA UdpConfigData;
+
+ UdpConfigData.AcceptBroadcast = TRUE;
+ UdpConfigData.AcceptPromiscuous = FALSE;
+ UdpConfigData.AcceptAnyPort = FALSE;
+ UdpConfigData.AllowDuplicatePort = TRUE;
+ UdpConfigData.TypeOfService = 0;
+ UdpConfigData.TimeToLive = 64;
+ UdpConfigData.DoNotFragment = FALSE;
+ UdpConfigData.ReceiveTimeout = 0;
+ UdpConfigData.TransmitTimeout = 0;
+
+ UdpConfigData.UseDefaultAddress = FALSE;
+ UdpConfigData.StationPort = DHCP_CLIENT_PORT;
+ UdpConfigData.RemotePort = DHCP_SERVER_PORT;
+
+ NetZeroMem (&UdpConfigData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
+ NetZeroMem (&UdpConfigData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ NetZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ return UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);;
+}
+
+
+
+/**
+ Destory the DHCP service. The Dhcp4 service may be partly initialized,
+ or partly destoried. If a resource is destoried, it is marked as so in
+ case the destory failed and being called again later.
+
+ @param DhcpSb The DHCP service instance to destory.
+
+ @retval EFI_SUCCESS The DHCP service is successfully closed.
+
+**/
+EFI_STATUS
+Dhcp4CloseService (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ DhcpCleanLease (DhcpSb);
+
+ if (DhcpSb->UdpIo != NULL) {
+ UdpIoFreePort (DhcpSb->UdpIo);
+ DhcpSb->UdpIo = NULL;
+ }
+
+ if (DhcpSb->Timer != NULL) {
+ gBS->SetTimer (DhcpSb->Timer, TimerCancel, 0);
+ gBS->CloseEvent (DhcpSb->Timer);
+
+ DhcpSb->Timer = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Create a new DHCP service binding instance for the controller.
+
+ @param Controller The controller to install DHCP service binding
+ protocol onto
+ @param ImageHandle The driver's image handle
+ @param Service The variable to receive the created DHCP service
+ instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource .
+ @retval EFI_SUCCESS The DHCP service instance is created.
+
+**/
+EFI_STATUS
+Dhcp4CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ OUT DHCP_SERVICE **Service
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+
+ *Service = NULL;
+ DhcpSb = NetAllocateZeroPool (sizeof (DHCP_SERVICE));
+
+ if (DhcpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DhcpSb->Signature = DHCP_SERVICE_SIGNATURE;
+ DhcpSb->ServiceBinding = mDhcp4ServiceBindingTemplete;
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+ DhcpSb->InDestory = FALSE;
+ DhcpSb->Controller = Controller;
+ DhcpSb->Image = ImageHandle;
+ NetListInit (&DhcpSb->Children);
+ DhcpSb->DhcpState = Dhcp4Stopped;
+ DhcpSb->Xid = NET_RANDOM (NetRandomInitSeed ());
+
+ //
+ // Create various resources, UdpIo, Timer, and get Mac address
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ DhcpOnTimerTick,
+ DhcpSb,
+ &DhcpSb->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ DhcpSb->UdpIo = UdpIoCreatePort (Controller, ImageHandle, DhcpConfigUdpIo, NULL);
+
+ if (DhcpSb->UdpIo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ DhcpSb->HwLen = (UINT8) DhcpSb->UdpIo->SnpMode.HwAddressSize;
+ DhcpSb->HwType = DhcpSb->UdpIo->SnpMode.IfType;
+ CopyMem (&DhcpSb->Mac, &DhcpSb->UdpIo->SnpMode.CurrentAddress, sizeof (EFI_MAC_ADDRESS));
+
+ *Service = DhcpSb;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Dhcp4CloseService (DhcpSb);
+ NetFreePool (DhcpSb);
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+
+ //
+ // First: test for the DHCP4 Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Dhcp4ServiceBinding Protocol onto ControlerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &DhcpSb->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return Status;
+
+ON_ERROR:
+ Dhcp4CloseService (DhcpSb);
+ NetFreePool (DhcpSb);
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero stop the entire bus driver.
+ @param ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCES This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // DHCP driver opens UDP child, So, the ControllerHandle is the
+ // UDP child handle. locate the Nic handle first.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding);
+
+ if (DhcpSb->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ DhcpSb->InDestory = TRUE;
+
+ //
+ // Don't use NET_LIST_FOR_EACH_SAFE here, Dhcp4ServiceBindingDestoryChild
+ // may cause other child to be deleted.
+ //
+ while (!NetListIsEmpty (&DhcpSb->Children)) {
+ Instance = NET_LIST_HEAD (&DhcpSb->Children, DHCP_PROTOCOL, Link);
+ Dhcp4ServiceBindingDestroyChild (ServiceBinding, Instance->Handle);
+ }
+
+ if (DhcpSb->NumChildren != 0) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ DhcpSb->ServiceState = DHCP_DESTORY;
+
+ Status = gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Dhcp4CloseService (DhcpSb);
+ NET_RESTORE_TPL (OldTpl);
+
+ NetFreePool (DhcpSb);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ DhcpSb->InDestory = FALSE;
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Initialize a new DHCP child
+
+ @param DhcpSb The dhcp service instance
+ @param Instance The dhcp instance to initialize
+
+ @return None
+
+**/
+VOID
+DhcpInitProtocol (
+ IN DHCP_SERVICE *DhcpSb,
+ IN DHCP_PROTOCOL *Instance
+ )
+{
+ Instance->Signature = DHCP_PROTOCOL_SIGNATURE;
+ CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (EFI_DHCP4_PROTOCOL));
+ NetListInit (&Instance->Link);
+ Instance->Handle = NULL;
+ Instance->Service = DhcpSb;
+ Instance->InDestory = FALSE;
+ Instance->CompletionEvent = NULL;
+ Instance->RenewRebindEvent = NULL;
+ Instance->Token = NULL;
+}
+
+
+/**
+ Creates a child handle with a set of DHCP4 services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If it
+ is not NULL, then the DHCP4 services are added to
+ the existing child handle.
+
+ @retval EFI_SUCCES The child handle was created with the DHCP4
+ services
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to create the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ VOID *Udp4;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = NetAllocatePool (sizeof (*Instance));
+
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DhcpSb = DHCP_SERVICE_FROM_THIS (This);
+ DhcpInitProtocol (DhcpSb, Instance);
+
+ //
+ // Install DHCP4 onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ &Instance->Dhcp4Protocol,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetFreePool (Instance);
+ return Status;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Udp4 protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ DhcpSb->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiDhcp4ProtocolGuid,
+ &Instance->Dhcp4Protocol,
+ NULL
+ );
+
+ NetFreePool (Instance);
+ return Status;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ NetListInsertTail (&DhcpSb->Children, &Instance->Link);
+ DhcpSb->NumChildren++;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Destroys a child handle with a set of DHCP4 services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The DHCP4 service is removed from the child handle
+ @retval EFI_UNSUPPORTED The child handle does not support the DHCP4
+ service
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its DHCP4 services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_DHCP4_PROTOCOL *Dhcp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **) &Dhcp,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (Dhcp);
+ DhcpSb = DHCP_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != DhcpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // A child can be destoried more than once. For example,
+ // Dhcp4DriverBindingStop will destory all of its children.
+ // when caller driver is being stopped, it will destory the
+ // dhcp child it opens.
+ //
+ if (Instance->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ Instance->InDestory = TRUE;
+
+ //
+ // Close the Udp4 protocol.
+ //
+ gBS->CloseProtocol (
+ DhcpSb->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the DHCP4 protocol first to enable a top down destruction.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ Dhcp
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->InDestory = FALSE;
+
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+ }
+
+ if (DhcpSb->ActiveChild == Instance) {
+ DhcpYieldControl (DhcpSb);
+ }
+
+ NetListRemoveEntry (&Instance->Link);
+ DhcpSb->NumChildren--;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ NetFreePool (Instance);
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h
new file mode 100644
index 0000000000..100bf56bb2
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h
@@ -0,0 +1,67 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Dhcp4Driver.h
+
+Abstract:
+
+ Header for the DHCP4 driver
+
+
+**/
+
+#ifndef __EFI_DHCP4_DRIVER_H__
+#define __EFI_DHCP4_DRIVER_H__
+
+extern EFI_COMPONENT_NAME_PROTOCOL gDhcp4ComponentName;
+
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf
new file mode 100644
index 0000000000..2c35be1f0e
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf
@@ -0,0 +1,65 @@
+#/** @file
+# Component name for module Dhcp4
+#
+# Copyright (c) 2007, Intel Corporation
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Dhcp4Dxe
+ FILE_GUID = 94734718-0BBC-47fb-96A5-EE7A5AE6A2AD
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = Dhcp4DriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ Dhcp4Impl.c
+ Dhcp4Io.c
+ Dhcp4Io.h
+ ComponentName.c
+ Dhcp4Driver.h
+ Dhcp4Driver.c
+ Dhcp4Option.c
+ Dhcp4Option.h
+ Dhcp4Impl.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ UdpIoLib
+
+
+[Protocols]
+ gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDhcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDhcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa
new file mode 100644
index 0000000000..6bde71ad29
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa
@@ -0,0 +1,77 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>Dhcp4Dxe</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>94734718-0BBC-47fb-96A5-EE7A5AE6A2AD</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Dhcp4</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>Dhcp4Dxe</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>Dhcp4Impl.h</Filename>
+ <Filename>Dhcp4Option.h</Filename>
+ <Filename>Dhcp4Option.c</Filename>
+ <Filename>Dhcp4Driver.c</Filename>
+ <Filename>Dhcp4Driver.h</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>Dhcp4Io.h</Filename>
+ <Filename>Dhcp4Io.c</Filename>
+ <Filename>Dhcp4Impl.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiDhcp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUdp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiDhcp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUdp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>Dhcp4DriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c
new file mode 100644
index 0000000000..68da959466
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c
@@ -0,0 +1,914 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Dhcp4Impl.c
+
+Abstract:
+
+ This file implement the EFI_DHCP4_PROTOCOL interface.
+
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+
+/**
+ Get the current operation parameter and lease for the network interface.
+
+ @param This The DHCP protocol instance
+ @param Dhcp4ModeData The variable to save the DHCP mode data.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid
+ @retval EFI_SUCCESS The Dhcp4ModeData is updated with the current
+ operation parameter.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4GetModeData (
+ IN EFI_DHCP4_PROTOCOL *This,
+ OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PARAMETER *Para;
+ EFI_TPL OldTpl;
+ IP4_ADDR Ip;
+
+ //
+ // First validate the parameters.
+ //
+ if ((This == NULL) || (Dhcp4ModeData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ DhcpSb = Instance->Service;
+
+ //
+ // Caller can use GetModeData to retrieve current DHCP states
+ // no matter whether it is the active child or not.
+ //
+ Dhcp4ModeData->State = DhcpSb->DhcpState;
+ CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (EFI_DHCP4_CONFIG_DATA));
+ CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (EFI_MAC_ADDRESS));
+
+ Ip = HTONL (DhcpSb->ClientAddr);
+ NetCopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Netmask);
+ NetCopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->ServerAddr);
+ NetCopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Para = DhcpSb->Para;
+
+ if (Para != NULL) {
+ Ip = HTONL (Para->Router);
+ NetCopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+ Dhcp4ModeData->LeaseTime = Para->Lease;
+ } else {
+ NetZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+ Dhcp4ModeData->LeaseTime = 0xffffffff;
+ }
+
+ Dhcp4ModeData->ReplyPacket = DhcpSb->Selected;
+
+ NET_RESTORE_TPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Free the resource related to the configure parameters.
+ DHCP driver will make a copy of the user's configure
+ such as the time out value.
+
+ @param Config The DHCP configure data
+
+ @return None
+
+**/
+VOID
+DhcpCleanConfigure (
+ IN EFI_DHCP4_CONFIG_DATA *Config
+ )
+{
+ UINT32 Index;
+
+ if (Config->DiscoverTimeout != NULL) {
+ NetFreePool (Config->DiscoverTimeout);
+ }
+
+ if (Config->RequestTimeout != NULL) {
+ NetFreePool (Config->RequestTimeout);
+ }
+
+ if (Config->OptionList != NULL) {
+ for (Index = 0; Index < Config->OptionCount; Index++) {
+ if (Config->OptionList[Index] != NULL) {
+ NetFreePool (Config->OptionList[Index]);
+ }
+ }
+
+ NetFreePool (Config->OptionList);
+ }
+
+ NetZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA));
+}
+
+
+/**
+ Allocate memory for configure parameter such as timeout value for Dst,
+ then copy the configure parameter from Src to Dst.
+
+ @param Dst The destination DHCP configure data.
+ @param Src The source DHCP configure data.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_SUCCESS The configure is copied.
+
+**/
+EFI_STATUS
+DhcpCopyConfigure (
+ IN EFI_DHCP4_CONFIG_DATA *Dst,
+ IN EFI_DHCP4_CONFIG_DATA *Src
+ )
+{
+ EFI_DHCP4_PACKET_OPTION **DstOptions;
+ EFI_DHCP4_PACKET_OPTION **SrcOptions;
+ INTN Len;
+ UINT32 Index;
+
+ CopyMem (Dst, Src, sizeof (EFI_DHCP4_CONFIG_DATA));
+ Dst->DiscoverTimeout = NULL;
+ Dst->RequestTimeout = NULL;
+ Dst->OptionList = NULL;
+
+ //
+ // Allocate a memory then copy DiscoverTimeout to it
+ //
+ if (Src->DiscoverTimeout != NULL) {
+ Len = Src->DiscoverTryCount * sizeof (UINT32);
+ Dst->DiscoverTimeout = NetAllocatePool (Len);
+
+ if (Dst->DiscoverTimeout == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < Src->DiscoverTryCount; Index++) {
+ Dst->DiscoverTimeout[Index] = NET_MAX (Src->DiscoverTimeout[Index], 1);
+ }
+ }
+
+ //
+ // Allocate a memory then copy RequestTimeout to it
+ //
+ if (Src->RequestTimeout != NULL) {
+ Len = Src->RequestTryCount * sizeof (UINT32);
+ Dst->RequestTimeout = NetAllocatePool (Len);
+
+ if (Dst->RequestTimeout == NULL) {
+ goto ON_ERROR;
+ }
+
+ for (Index = 0; Index < Src->RequestTryCount; Index++) {
+ Dst->RequestTimeout[Index] = NET_MAX (Src->RequestTimeout[Index], 1);
+ }
+ }
+
+ //
+ // Allocate an array of dhcp option point, then allocate memory
+ // for each option and copy the source option to it
+ //
+ if (Src->OptionList != NULL) {
+ Len = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *);
+ Dst->OptionList = NetAllocateZeroPool (Len);
+
+ if (Dst->OptionList == NULL) {
+ goto ON_ERROR;
+ }
+
+ DstOptions = Dst->OptionList;
+ SrcOptions = Src->OptionList;
+
+ for (Index = 0; Index < Src->OptionCount; Index++) {
+ Len = sizeof (EFI_DHCP4_PACKET_OPTION) + NET_MAX (SrcOptions[Index]->Length - 1, 0);
+
+ DstOptions[Index] = NetAllocatePool (Len);
+
+ if (DstOptions[Index] == NULL) {
+ goto ON_ERROR;
+ }
+
+ NetCopyMem (DstOptions[Index], SrcOptions[Index], Len);
+ }
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ DhcpCleanConfigure (Dst);
+ return EFI_OUT_OF_RESOURCES;
+}
+
+
+/**
+ Give up the control of the DHCP service to let other child
+ resume. Don't change the service's DHCP state and the Client
+ address and option list configure as required by RFC2131.
+
+ @param DhcpSb The DHCP service instance.
+
+ @return None
+
+**/
+VOID
+DhcpYieldControl (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ DHCP_PROTOCOL *Instance;
+
+ Instance = DhcpSb->ActiveChild;
+ Config = &DhcpSb->ActiveConfig;
+
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+ DhcpSb->ActiveChild = NULL;
+
+ if (Config->DiscoverTimeout != NULL) {
+ NetFreePool (Config->DiscoverTimeout);
+
+ Config->DiscoverTryCount = 0;
+ Config->DiscoverTimeout = NULL;
+ }
+
+ if (Config->RequestTimeout != NULL) {
+ NetFreePool (Config->RequestTimeout);
+
+ Config->RequestTryCount = 0;
+ Config->RequestTimeout = NULL;
+ }
+
+ Config->Dhcp4Callback = NULL;
+ Config->CallbackContext = NULL;
+}
+
+
+/**
+ Configure the DHCP protocol instance and its underlying DHCP service
+ for operation. If Dhcp4CfgData is NULL and the child is currently
+ controlling the DHCP service, release the control.
+
+ @param This The DHCP protocol instance
+ @param Dhcp4CfgData The DHCP configure data.
+
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_ACCESS_DENIED The service isn't in one of configurable states,
+ or there is already an active child.
+ @retval EFI_OUT_OF_RESOURCE Failed to allocate some resources.
+ @retval EFI_SUCCESS The child is configured.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4Configure (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ UINT32 Index;
+ IP4_ADDR Ip;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp4CfgData != NULL) {
+ if (Dhcp4CfgData->DiscoverTryCount && (Dhcp4CfgData->DiscoverTimeout == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp4CfgData->RequestTryCount && (Dhcp4CfgData->RequestTimeout == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp4CfgData->OptionCount && (Dhcp4CfgData->OptionList == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NetCopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR));
+
+ if ((Ip != 0) && !Ip4IsUnicast (NTOHL (Ip), 0)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ DhcpSb = Instance->Service;
+ Config = &DhcpSb->ActiveConfig;
+
+ Status = EFI_ACCESS_DENIED;
+
+ if ((DhcpSb->DhcpState != Dhcp4Stopped) &&
+ (DhcpSb->DhcpState != Dhcp4Init) &&
+ (DhcpSb->DhcpState != Dhcp4InitReboot) &&
+ (DhcpSb->DhcpState != Dhcp4Bound)) {
+
+ goto ON_EXIT;
+ }
+
+ if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {
+ goto ON_EXIT;
+ }
+
+ if (Dhcp4CfgData != NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DhcpCleanConfigure (Config);
+
+ if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {
+ goto ON_EXIT;
+ }
+
+ DhcpSb->UserOptionLen = 0;
+
+ for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {
+ DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;
+ }
+
+ DhcpSb->ActiveChild = Instance;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);
+
+ if (DhcpSb->ClientAddr != 0) {
+ DhcpSb->DhcpState = Dhcp4InitReboot;
+ } else {
+ DhcpSb->DhcpState = Dhcp4Init;
+ }
+ }
+
+ DhcpSb->ServiceState = DHCP_CONFIGED;
+ Status = EFI_SUCCESS;
+
+ } else if (DhcpSb->ActiveChild == Instance) {
+ Status = EFI_SUCCESS;
+ DhcpYieldControl (DhcpSb);
+ }
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Start the DHCP process.
+
+ @param This The DHCP protocol instance
+ @param CompletionEvent The event to signal is address is acquired.
+
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_NOT_STARTED The protocol hasn't been configured.
+ @retval EFI_ALREADY_STARTED The DHCP process has already been started.
+ @retval EFI_SUCCESS The DHCP process is started.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4Start (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ DhcpSb = Instance->Service;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ Status = EFI_NOT_STARTED;
+ goto ON_ERROR;
+ }
+
+ if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {
+ Status = EFI_ALREADY_STARTED;
+ goto ON_ERROR;
+ }
+
+ DhcpSb->IoStatus = EFI_ALREADY_STARTED;
+
+ if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Start/Restart the receiving.
+ //
+ Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ Instance->CompletionEvent = CompletionEvent;
+
+ //
+ // Restore the TPL now, don't call poll function at NET_TPL_LOCK.
+ //
+ NET_RESTORE_TPL (OldTpl);
+
+ if (CompletionEvent == NULL) {
+ while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
+ DhcpSb->UdpIo->Udp->Poll (DhcpSb->UdpIo->Udp);
+ }
+
+ return DhcpSb->IoStatus;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Request an extra manual renew/rebind.
+
+ @param This The DHCP protocol instance
+ @param RebindRequest TRUE if request a rebind, otherwise renew it
+ @param CompletionEvent Event to signal when complete
+
+ @retval EFI_INVALID_PARAMETER The parameters are invalid
+ @retval EFI_NOT_STARTED The DHCP protocol hasn't been started.
+ @retval EFI_ACCESS_DENIED The DHCP protocol isn't in Bound state.
+ @retval EFI_SUCCESS The DHCP is renewed/rebound.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4RenewRebind (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN BOOLEAN RebindRequest,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ DhcpSb = Instance->Service;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ Status = EFI_NOT_STARTED;
+ goto ON_ERROR;
+ }
+
+ if (DhcpSb->DhcpState != Dhcp4Bound) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_ERROR;
+ }
+
+ if (DHCP_IS_BOOTP (DhcpSb->Para)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Transit the states then send a extra DHCP request
+ //
+ if (!RebindRequest) {
+ DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
+ } else {
+ DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
+ }
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ "Extra renew/rebind by the application"
+ );
+
+ if (EFI_ERROR (Status)) {
+ DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
+ goto ON_ERROR;
+ }
+
+ DhcpSb->ExtraRefresh = TRUE;
+ DhcpSb->IoStatus = EFI_ALREADY_STARTED;
+ Instance->RenewRebindEvent = CompletionEvent;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ if (CompletionEvent == NULL) {
+ while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
+ DhcpSb->UdpIo->Udp->Poll (DhcpSb->UdpIo->Udp);
+ }
+
+ return DhcpSb->IoStatus;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Release the current acquired lease.
+
+ @param This The DHCP protocol instance
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid
+ @retval EFI_DEVICE_ERROR Failed to transmit the DHCP release packet
+ @retval EFI_ACCESS_DENIED The DHCP service isn't in one of the connected
+ state.
+ @retval EFI_SUCCESS The lease is released.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4Release (
+ IN EFI_DHCP4_PROTOCOL *This
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ DhcpSb = Instance->Service;
+
+ if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_RELEASE,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ }
+
+ DhcpCleanLease (DhcpSb);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Stop the current DHCP process. After this, other DHCP child
+ can gain control of the service, configure and use it.
+
+ @param This The DHCP protocol instance
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_SUCCESS The DHCP process is stopped.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4Stop (
+ IN EFI_DHCP4_PROTOCOL *This
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ DhcpSb = Instance->Service;
+
+ DhcpCleanLease (DhcpSb);
+
+ DhcpSb->DhcpState = Dhcp4Stopped;
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+
+ NET_RESTORE_TPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Build a new DHCP packet from the seed packet. Options may be deleted or
+ appended. The caller should free the NewPacket when finished using it.
+
+ @param This The DHCP protocol instance.
+ @param SeedPacket The seed packet to start with
+ @param DeleteCount The number of options to delete
+ @param DeleteList The options to delete from the packet
+ @param AppendCount The number of options to append
+ @param AppendList The options to append to the packet
+ @param NewPacket The new packet, allocated and built by this
+ function.
+
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval EFI_SUCCESS The packet is build.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4Build (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ )
+{
+ //
+ // First validate the parameters
+ //
+ if ((This == NULL) || (NewPacket == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
+ EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((DeleteCount == 0) && (AppendCount == 0)) ||
+ ((DeleteCount != 0) && (DeleteList == NULL)) ||
+ ((AppendCount != 0) && (AppendList == NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return DhcpBuild (
+ SeedPacket,
+ DeleteCount,
+ DeleteList,
+ AppendCount,
+ AppendList,
+ NewPacket
+ );
+}
+
+
+/**
+ Transmit and receive a packet through this DHCP service.
+ This is unsupported.
+
+ @param This The DHCP protocol instance
+ @param Token The transmit and receive instance
+
+ @retval EFI_UNSUPPORTED It always returns unsupported.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4TransmitReceive (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token
+ )
+{
+ //
+ // This function is for PXE, leave it for now
+ //
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Callback function for DhcpIterateOptions. This callback sets the
+ EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point
+ the individual DHCP option in the packet.
+
+ @param Tag The DHCP option type
+ @param Len length of the DHCP option data
+ @param Data The DHCP option data
+ @param Context The context, to pass several parameters in.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS
+
+**/
+STATIC
+EFI_STATUS
+Dhcp4ParseCheckOption (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_PARSE_CONTEXT *Parse;
+
+ Parse = (DHCP_PARSE_CONTEXT *) Context;
+ Parse->Index++;
+
+ if (Parse->Index < Parse->OptionCount) {
+ //
+ // Use _CR to get the memory position of EFI_DHCP4_PACKET_OPTION for
+ // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only
+ // pass in the point to option data.
+ //
+ Parse->Option[Parse->Index - 1] = _CR (Data, EFI_DHCP4_PACKET_OPTION, Data);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the DHCP options in the Packet into the PacketOptionList.
+ User should allocate this array of EFI_DHCP4_PACKET_OPTION points.
+
+ @param This The DHCP protocol instance
+ @param Packet The DHCP packet to parse
+ @param OptionCount On input, the size of the PacketOptionList; On
+ output, the actual number of options processed.
+ @param PacketOptionList The array of EFI_DHCP4_PACKET_OPTION points
+
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_BUFFER_TOO_SMALL A bigger array of points is needed.
+ @retval EFI_SUCCESS The options are parsed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiDhcp4Parse (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ )
+{
+ DHCP_PARSE_CONTEXT Context;
+ EFI_STATUS Status;
+
+ //
+ // First validate the parameters
+ //
+ if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||
+ (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
+ EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ NetZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
+
+ Context.Option = PacketOptionList;
+ Context.OptionCount = *OptionCount;
+ Context.Index = 0;
+
+ Status = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *OptionCount = Context.Index;
+
+ if (Context.Index > Context.OptionCount) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate = {
+ EfiDhcp4GetModeData,
+ EfiDhcp4Configure,
+ EfiDhcp4Start,
+ EfiDhcp4RenewRebind,
+ EfiDhcp4Release,
+ EfiDhcp4Stop,
+ EfiDhcp4Build,
+ EfiDhcp4TransmitReceive,
+ EfiDhcp4Parse
+};
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h
new file mode 100644
index 0000000000..7f449415d2
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h
@@ -0,0 +1,159 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Dhcp4Impl.h
+
+Abstract:
+
+ EFI DHCP protocol implementation
+ RFCs supported are:
+ RFC 2131: Dynamic Host Configuration Protocol
+ RFC 2132: DHCP Options and BOOTP Vendor Extensions
+ RFC 1534: Interoperation Between DHCP and BOOTP
+ RFC 3396: Encoding Long Options in DHCP
+
+
+**/
+
+#ifndef __EFI_DHCP4_IMPL_H__
+#define __EFI_DHCP4_IMPL_H__
+
+
+
+#include <PiDxe.h>
+
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Udp4.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+
+typedef struct _DHCP_SERVICE DHCP_SERVICE;
+typedef struct _DHCP_PROTOCOL DHCP_PROTOCOL;
+
+#include "Dhcp4Option.h"
+#include "Dhcp4Io.h"
+
+enum {
+ DHCP_SERVICE_SIGNATURE = EFI_SIGNATURE_32 ('D', 'H', 'C', 'P'),
+ DHCP_PROTOCOL_SIGNATURE = EFI_SIGNATURE_32 ('d', 'h', 'c', 'p'),
+
+ //
+ // The state of the DHCP service. It starts as UNCONFIGED. If
+ // and active child configures the service successfully, it
+ // goes to CONFIGED. If the active child configures NULL, it
+ // goes back to UNCONFIGED. It becomes DESTORY if it is (partly)
+ // destoried.
+ //
+ DHCP_UNCONFIGED = 0,
+ DHCP_CONFIGED,
+ DHCP_DESTORY,
+};
+
+typedef struct _DHCP_PROTOCOL {
+ UINT32 Signature;
+ EFI_DHCP4_PROTOCOL Dhcp4Protocol;
+ NET_LIST_ENTRY Link;
+ EFI_HANDLE Handle;
+ DHCP_SERVICE *Service;
+
+ BOOLEAN InDestory;
+
+ EFI_EVENT CompletionEvent;
+ EFI_EVENT RenewRebindEvent;
+
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+};
+
+//
+// DHCP driver is specical in that it is a singleton. Although it
+// has a service binding, there can be only one active child.
+//
+typedef struct _DHCP_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ INTN ServiceState; // CONFIGED, UNCONFIGED, and DESTORY
+ BOOLEAN InDestory;
+
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ NET_LIST_ENTRY Children;
+ UINTN NumChildren;
+
+ INTN DhcpState;
+ EFI_STATUS IoStatus; // the result of last user operation
+ UINT32 Xid;
+
+ IP4_ADDR ClientAddr; // lease IP or configured client address
+ IP4_ADDR Netmask;
+ IP4_ADDR ServerAddr;
+
+ EFI_DHCP4_PACKET *LastOffer; // The last received offer
+ EFI_DHCP4_PACKET *Selected;
+ DHCP_PARAMETER *Para;
+
+ UINT32 Lease;
+ UINT32 T1;
+ UINT32 T2;
+ INTN ExtraRefresh; // This refresh is reqested by user
+
+ UDP_IO_PORT *UdpIo; // Udp child receiving all DHCP message
+ UDP_IO_PORT *LeaseIoPort; // Udp child with lease IP
+ NET_BUF *LastPacket; // The last sent packet for retransmission
+ EFI_MAC_ADDRESS Mac;
+ UINT8 HwType;
+ UINT8 HwLen;
+
+ DHCP_PROTOCOL *ActiveChild;
+ EFI_DHCP4_CONFIG_DATA ActiveConfig;
+ UINT32 UserOptionLen;
+
+ //
+ // Timer event and various timer
+ //
+ EFI_EVENT Timer;
+
+ UINT32 PacketToLive; // Retransmission timer for our packets
+ INTN CurRetry;
+ INTN MaxRetries;
+
+ UINT32 WaitOffer; // Time to collect the offers
+ UINT32 LeaseLife;
+};
+
+typedef struct {
+ EFI_DHCP4_PACKET_OPTION **Option;
+ UINT32 OptionCount;
+ UINT32 Index;
+} DHCP_PARSE_CONTEXT;
+
+#define DHCP_INSTANCE_FROM_THIS(Proto) \
+ CR ((Proto), DHCP_PROTOCOL, Dhcp4Protocol, DHCP_PROTOCOL_SIGNATURE)
+
+#define DHCP_SERVICE_FROM_THIS(Sb) \
+ CR ((Sb), DHCP_SERVICE, ServiceBinding, DHCP_SERVICE_SIGNATURE)
+
+extern EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate;
+
+VOID
+DhcpYieldControl (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
new file mode 100644
index 0000000000..e0fdcd382c
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
@@ -0,0 +1,1631 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Dhcp4Io.c
+
+Abstract:
+
+ EFI DHCP protocol implementation
+
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+UINT32 mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 };
+
+
+/**
+ Send an initial DISCOVER or REQUEST message according to the
+ DHCP service's current state.
+
+ @param DhcpSb The DHCP service instance
+
+ @retval EFI_SUCCESS The request has been sent
+
+**/
+EFI_STATUS
+DhcpInitRequest (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot));
+
+ if (DhcpSb->DhcpState == Dhcp4Init) {
+ DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE);
+ Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL);
+
+ if (EFI_ERROR (Status)) {
+ DhcpSb->DhcpState = Dhcp4Init;
+ return Status;
+ }
+
+ DhcpSb->WaitOffer = DHCP_WAIT_OFFER;
+ } else {
+ DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE);
+ Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL);
+
+ if (EFI_ERROR (Status)) {
+ DhcpSb->DhcpState = Dhcp4InitReboot;
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call user provided callback function, and return the value the
+ function returns. If the user doesn't provide a callback, a
+ proper return value is selected to let the caller continue the
+ normal process.
+
+ @param DhcpSb The DHCP service instance
+ @param Event The event as defined in the spec
+ @param Packet The current packet trigger the event
+ @param NewPacket The user's return new packet
+
+ @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
+ @retval EFI_SUCCESS The user function returns success.
+ @retval EFI_ABORTED The user function ask it to abort.
+
+**/
+STATIC
+EFI_STATUS
+DhcpCallUser (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_EVENT Event,
+ IN EFI_DHCP4_PACKET *Packet, OPTIONAL
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ EFI_STATUS Status;
+
+ if (NewPacket != NULL) {
+ *NewPacket = NULL;
+ }
+
+ //
+ // If user doesn't provide the call back function, return the value
+ // that directs the client to continue the normal process.
+ // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
+ // the offers and select a offer, EFI_NOT_READY tells the client to
+ // collect more offers.
+ //
+ Config = &DhcpSb->ActiveConfig;
+
+ if (Config->Dhcp4Callback == NULL) {
+ if (Event == Dhcp4RcvdOffer) {
+ return EFI_NOT_READY;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ Status = Config->Dhcp4Callback (
+ &DhcpSb->ActiveChild->Dhcp4Protocol,
+ Config->CallbackContext,
+ DhcpSb->DhcpState,
+ Event,
+ Packet,
+ NewPacket
+ );
+
+ //
+ // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
+ // and EFI_ABORTED. If it returns values other than those, assume
+ // it to be EFI_ABORTED.
+ //
+ if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) {
+ return Status;
+ }
+
+ return EFI_ABORTED;
+}
+
+
+/**
+ Notify the user about the operation result.
+
+ @param DhcpSb DHCP service instance
+ @param Which which notify function to signal
+
+ @return None
+
+**/
+VOID
+DhcpNotifyUser (
+ IN DHCP_SERVICE *DhcpSb,
+ IN INTN Which
+ )
+{
+ DHCP_PROTOCOL *Child;
+
+ if ((Child = DhcpSb->ActiveChild) == NULL) {
+ return ;
+ }
+
+ if ((Child->CompletionEvent != NULL) &&
+ ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))) {
+
+ gBS->SignalEvent (Child->CompletionEvent);
+ Child->CompletionEvent = NULL;
+ }
+
+ if ((Child->RenewRebindEvent != NULL) &&
+ ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))) {
+
+ gBS->SignalEvent (Child->RenewRebindEvent);
+ Child->RenewRebindEvent = NULL;
+ }
+}
+
+
+
+/**
+ Set the DHCP state. If CallUser is true, it will try to notify
+ the user before change the state by DhcpNotifyUser. It returns
+ EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
+ EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
+ the return value of this function.
+
+ @param DhcpSb The DHCP service instance
+ @param State The new DHCP state to change to
+ @param CallUser Whether we need to call user
+
+ @retval EFI_SUCCESS The state is changed
+ @retval EFI_ABORTED The user asks to abort the DHCP process.
+
+**/
+EFI_STATUS
+DhcpSetState (
+ IN DHCP_SERVICE *DhcpSb,
+ IN INTN State,
+ IN BOOLEAN CallUser
+ )
+{
+ EFI_STATUS Status;
+
+ if (CallUser) {
+ Status = EFI_SUCCESS;
+
+ if (State == Dhcp4Renewing) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL);
+
+ } else if (State == Dhcp4Rebinding) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL);
+
+ } else if (State == Dhcp4Bound) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL);
+
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Update the retransmission timer during the state transition.
+ // This will clear the retry count. This is also why the rule
+ // first transit the state, then send packets.
+ //
+ if (State == Dhcp4Init) {
+ DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount;
+ } else {
+ DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount;
+ }
+
+ if (DhcpSb->MaxRetries == 0) {
+ DhcpSb->MaxRetries = 4;
+ }
+
+ DhcpSb->CurRetry = 0;
+ DhcpSb->PacketToLive = 0;
+
+ DhcpSb->DhcpState = State;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Set the retransmit timer for the packet. It will select from either
+ the discover timeouts/request timeouts or the default timeout values.
+
+ @param DhcpSb The DHCP service instance.
+
+ @return None
+
+**/
+STATIC
+VOID
+DhcpSetTransmitTimer (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ UINT32 *Times;
+
+ ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);
+
+ if (DhcpSb->DhcpState == Dhcp4Init) {
+ Times = DhcpSb->ActiveConfig.DiscoverTimeout;
+ } else {
+ Times = DhcpSb->ActiveConfig.RequestTimeout;
+ }
+
+ if (Times == NULL) {
+ Times = mDhcp4DefaultTimeout;
+ }
+
+ DhcpSb->PacketToLive = Times[DhcpSb->CurRetry];
+}
+
+
+/**
+ Compute the lease. If the server grants a permanent lease, just
+ process it as a normal timeout value since the lease will last
+ more than 100 years.
+
+ @param DhcpSb The DHCP service instance
+ @param Para The DHCP parameter extracted from the server's
+ response.
+
+ @return None
+
+**/
+STATIC
+VOID
+DhcpComputeLease (
+ IN DHCP_SERVICE *DhcpSb,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ ASSERT (Para != NULL);
+
+ DhcpSb->Lease = Para->Lease;
+ DhcpSb->T2 = Para->T2;
+ DhcpSb->T1 = Para->T1;
+
+ if (DhcpSb->Lease == 0) {
+ DhcpSb->Lease = DHCP_DEFAULT_LEASE;
+ }
+
+ if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) {
+ DhcpSb->T2 = Para->Lease - (Para->Lease >> 3);
+ }
+
+ if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) {
+ DhcpSb->T1 = DhcpSb->Lease >> 1;
+ }
+}
+
+
+/**
+ Configure a UDP IO port to use the acquired lease address.
+ DHCP driver needs this port to unicast packet to the server
+ such as DHCP release.
+
+ @param UdpIo The UDP IO port to configure
+ @param Context The opaque parameter to the function.
+
+ @retval EFI_SUCCESS The UDP IO port is successfully configured.
+ @retval Others It failed to configure the port.
+
+**/
+EFI_STATUS
+DhcpConfigLeaseIoPort (
+ IN UDP_IO_PORT *UdpIo,
+ IN VOID *Context
+ )
+{
+ EFI_UDP4_CONFIG_DATA UdpConfigData;
+ EFI_IPv4_ADDRESS Subnet;
+ EFI_IPv4_ADDRESS Gateway;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ DhcpSb = (DHCP_SERVICE *) Context;
+
+ UdpConfigData.AcceptBroadcast = FALSE;
+ UdpConfigData.AcceptPromiscuous = FALSE;
+ UdpConfigData.AcceptAnyPort = FALSE;
+ UdpConfigData.AllowDuplicatePort = TRUE;
+ UdpConfigData.TypeOfService = 0;
+ UdpConfigData.TimeToLive = 64;
+ UdpConfigData.DoNotFragment = FALSE;
+ UdpConfigData.ReceiveTimeout = 1;
+ UdpConfigData.TransmitTimeout = 0;
+
+ UdpConfigData.UseDefaultAddress = FALSE;
+ UdpConfigData.StationPort = DHCP_CLIENT_PORT;
+ UdpConfigData.RemotePort = DHCP_SERVER_PORT;
+
+ Ip = HTONL (DhcpSb->ClientAddr);
+ NetCopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Netmask);
+ NetCopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ NetZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ Status = UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Add a default route if received from the server.
+ //
+ if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {
+ NetZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Para->Router);
+ NetCopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ UdpIo->Udp->Routes (UdpIo->Udp, FALSE, &Subnet, &Subnet, &Gateway);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Update the lease states when a new lease is acquired. It will not only
+ save the acquired the address and lease time, it will also create a UDP
+ child to provide address resolution for the address.
+
+ @param DhcpSb The DHCP service instance
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_SUCCESS The lease is recorded.
+
+**/
+STATIC
+EFI_STATUS
+DhcpLeaseAcquired (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ INTN Class;
+
+ DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
+
+ if (DhcpSb->Para != NULL) {
+ DhcpSb->Netmask = DhcpSb->Para->NetMask;
+ DhcpSb->ServerAddr = DhcpSb->Para->ServerId;
+ }
+
+ if (DhcpSb->Netmask == 0) {
+ Class = NetGetIpClass (DhcpSb->ClientAddr);
+ DhcpSb->Netmask = mIp4AllMasks[Class << 3];
+ }
+
+ if (DhcpSb->LeaseIoPort != NULL) {
+ UdpIoFreePort (DhcpSb->LeaseIoPort);
+ }
+
+ //
+ // Create a UDP/IP child to provide ARP service for the Leased IP,
+ // and transmit unicast packet with it as source address. Don't
+ // start receive on this port, the queued packet will be timeout.
+ //
+ DhcpSb->LeaseIoPort = UdpIoCreatePort (
+ DhcpSb->Controller,
+ DhcpSb->Image,
+ DhcpConfigLeaseIoPort,
+ DhcpSb
+ );
+
+ if (DhcpSb->LeaseIoPort == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
+ DhcpComputeLease (DhcpSb, DhcpSb->Para);
+ }
+
+ return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
+}
+
+
+/**
+ Clean up the DHCP related states, IoStatus isn't reset.
+
+ @param DhcpSb The DHCP instance service.
+
+ @return None
+
+**/
+VOID
+DhcpCleanLease (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ DhcpSb->DhcpState = Dhcp4Init;
+ DhcpSb->Xid = DhcpSb->Xid + 1;
+ DhcpSb->ClientAddr = 0;
+ DhcpSb->ServerAddr = 0;
+
+ if (DhcpSb->LastOffer != NULL) {
+ NetFreePool (DhcpSb->LastOffer);
+ DhcpSb->LastOffer = NULL;
+ }
+
+ if (DhcpSb->Selected != NULL) {
+ NetFreePool (DhcpSb->Selected);
+ DhcpSb->Selected = NULL;
+ }
+
+ if (DhcpSb->Para != NULL) {
+ NetFreePool (DhcpSb->Para);
+ DhcpSb->Para = NULL;
+ }
+
+ DhcpSb->Lease = 0;
+ DhcpSb->T1 = 0;
+ DhcpSb->T2 = 0;
+ DhcpSb->ExtraRefresh = FALSE;
+
+ if (DhcpSb->LeaseIoPort != NULL) {
+ UdpIoFreePort (DhcpSb->LeaseIoPort);
+ DhcpSb->LeaseIoPort = NULL;
+ }
+
+ if (DhcpSb->LastPacket != NULL) {
+ NetbufFree (DhcpSb->LastPacket);
+ DhcpSb->LastPacket = NULL;
+ }
+
+ DhcpSb->PacketToLive = 0;
+ DhcpSb->CurRetry = 0;
+ DhcpSb->MaxRetries = 0;
+ DhcpSb->WaitOffer = 0;
+ DhcpSb->LeaseLife = 0;
+}
+
+
+/**
+ Select a offer among all the offers collected. If the offer selected is
+ of BOOTP, the lease is recorded and user notified. If the offer is of
+ DHCP, it will request the offer from the server.
+
+ @param DhcpSb The DHCP service instance.
+
+ @retval EFI_SUCCESS One of the offer is selected.
+
+**/
+STATIC
+EFI_STATUS
+DhcpChooseOffer (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_DHCP4_PACKET *Selected;
+ EFI_DHCP4_PACKET *NewPacket;
+ EFI_STATUS Status;
+
+ ASSERT (DhcpSb->LastOffer != NULL);
+
+ //
+ // Stop waiting more offers
+ //
+ DhcpSb->WaitOffer = 0;
+
+ //
+ // User will cache previous offers if he wants to select
+ // from multiple offers. If user provides an invalid packet,
+ // use the last offer, otherwise use the provided packet.
+ //
+ NewPacket = NULL;
+ Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Selected = DhcpSb->LastOffer;
+
+ if (NewPacket != NULL) {
+ if (EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
+ NetFreePool (NewPacket);
+ } else {
+ NetFreePool (Selected);
+ Selected = NewPacket;
+ }
+ }
+
+ DhcpSb->Selected = Selected;
+ DhcpSb->LastOffer = NULL;
+ DhcpSb->Para = NULL;
+ DhcpValidateOptions (Selected, &DhcpSb->Para);
+
+ //
+ // A bootp offer has been selected, save the lease status,
+ // enter bound state then notify the user.
+ //
+ if (DHCP_IS_BOOTP (DhcpSb->Para)) {
+ Status = DhcpLeaseAcquired (DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Send a DHCP requests
+ //
+ Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
+}
+
+
+/**
+ Terminate the current address acquire. All the allocated resources
+ are released. Be careful when calling this function. A rule related
+ to this is: only call DhcpEndSession at the highest level, such as
+ DhcpInput, DhcpOnTimerTick...At the other level, just return error.
+
+ @param DhcpSb The DHCP service instance
+ @param Status The result of the DHCP process.
+
+ @return None
+
+**/
+STATIC
+VOID
+DhcpEndSession (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_STATUS Status
+ )
+{
+ if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
+ DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
+ } else {
+ DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
+ }
+
+ DhcpCleanLease (DhcpSb);
+
+ DhcpSb->IoStatus = Status;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
+}
+
+
+/**
+ Handle packets in DHCP select state.
+
+ @param DhcpSb The DHCP service instance
+ @param Packet The DHCP packet received
+ @param Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occured.
+
+**/
+STATIC
+EFI_STATUS
+DhcpHandleSelect (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // First validate the message:
+ // 1. the offer is a unicast
+ // 2. if it is a DHCP message, it must contains a server ID.
+ // Don't return a error for these two case otherwise the session is ended.
+ //
+ Head = &Packet->Dhcp4.Header;
+
+ if (!Ip4IsUnicast (EFI_NTOHL (Head->YourAddr), (Para == NULL ? 0 : Para->NetMask))) {
+ goto ON_EXIT;
+ }
+
+ if (!DHCP_IS_BOOTP (Para) &&
+ ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Call the user's callback. The action according to the return is as:
+ // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
+ // 2. EFI_NOT_READY: wait for more offers
+ // 3. EFI_ABORTED: abort the address acquiring.
+ //
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
+
+ if (Status == EFI_SUCCESS) {
+ if (DhcpSb->LastOffer != NULL) {
+ NetFreePool (DhcpSb->LastOffer);
+ }
+
+ DhcpSb->LastOffer = Packet;
+
+ return DhcpChooseOffer (DhcpSb);
+
+ } else if (Status == EFI_NOT_READY) {
+ if (DhcpSb->LastOffer != NULL) {
+ NetFreePool (DhcpSb->LastOffer);
+ }
+
+ DhcpSb->LastOffer = Packet;
+
+ } else if (Status == EFI_ABORTED) {
+ //
+ // DhcpInput will end the session upon error return. Remember
+ // only to call DhcpEndSession at the top level call.
+ //
+ goto ON_EXIT;
+ }
+
+ return EFI_SUCCESS;
+
+ON_EXIT:
+ NetFreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP request state.
+
+ @param DhcpSb The DHCP service instance
+ @param Packet The DHCP packet received
+ @param Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occured.
+
+**/
+STATIC
+EFI_STATUS
+DhcpHandleRequest (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *Selected;
+ EFI_STATUS Status;
+ UINT8 *Message;
+
+ ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
+
+ Head = &Packet->Dhcp4.Header;
+ Selected = &DhcpSb->Selected->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ (Para->ServerId != DhcpSb->Para->ServerId) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Received a NAK, end the session no matter what the user returns
+ //
+ Status = EFI_DEVICE_ERROR;
+
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the ACK matches the selected offer
+ //
+ Message = NULL;
+
+ if (!EFI_IP4_EQUAL (Head->YourAddr, Selected->YourAddr)) {
+ Message = "Lease confirmed isn't the same as that in the offer";
+ goto REJECT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+
+ if (EFI_ERROR (Status)) {
+ Message = "Lease is denied upon received ACK";
+ goto REJECT;
+ }
+
+ //
+ // Record the lease, transit to BOUND state, then notify the user
+ //
+ Status = DhcpLeaseAcquired (DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ Message = "Lease is denied upon entering bound";
+ goto REJECT;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
+
+ NetFreePool (Packet);
+ return EFI_SUCCESS;
+
+REJECT:
+ DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
+
+ON_EXIT:
+ NetFreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP renew/rebound state.
+
+ @param DhcpSb The DHCP service instance
+ @param Packet The DHCP packet received
+ @param Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occured.
+
+**/
+STATIC
+EFI_STATUS
+DhcpHandleRenewRebind (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *Selected;
+ EFI_STATUS Status;
+
+ ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
+
+ Head = &Packet->Dhcp4.Header;
+ Selected = &DhcpSb->Selected->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ (Para->ServerId != DhcpSb->Para->ServerId) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Received a NAK, ignore the user's return then terminate the process
+ //
+ Status = EFI_DEVICE_ERROR;
+
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // The lease is different from the selected. Don't send a DECLINE
+ // since it isn't existed in the client's FSM.
+ //
+ if (!EFI_IP4_EQUAL (Head->YourAddr, Selected->YourAddr)) {
+ goto ON_EXIT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Record the lease, start timer for T1 and T2,
+ //
+ DhcpComputeLease (DhcpSb, Para);
+ DhcpSb->LeaseLife = 0;
+ DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
+
+ if (DhcpSb->ExtraRefresh) {
+ DhcpSb->ExtraRefresh = FALSE;
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
+ }
+
+ON_EXIT:
+ NetFreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP reboot state.
+
+ @param DhcpSb The DHCP service instance
+ @param Packet The DHCP packet received
+ @param Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occured.
+
+**/
+STATIC
+EFI_STATUS
+DhcpHandleReboot (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_STATUS Status;
+
+ Head = &Packet->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // If a NAK is received, transit to INIT and try again.
+ //
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+
+ DhcpSb->ClientAddr = 0;
+ DhcpSb->DhcpState = Dhcp4Init;
+
+ Status = DhcpInitRequest (DhcpSb);
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the ACK matches the selected offer
+ //
+ if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // OK, get the parameter from server, record the lease
+ //
+ DhcpSb->Para = NetAllocatePool (sizeof (DHCP_PARAMETER));
+
+ if (DhcpSb->Para == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ DhcpSb->Selected = Packet;
+ CopyMem (DhcpSb->Para, Para, sizeof (DHCP_PARAMETER));
+
+ Status = DhcpLeaseAcquired (DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
+ return EFI_SUCCESS;
+
+ON_EXIT:
+ NetFreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle the received DHCP packets. This function drivers the DHCP
+ state machine.
+
+ @param UdpPacket The UDP packets received.
+ @param Points The local/remote UDP access points
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+ @return None
+
+**/
+VOID
+DhcpInput (
+ NET_BUF *UdpPacket,
+ UDP_POINTS *Points,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_PACKET *Packet;
+ DHCP_PARAMETER *Para;
+ EFI_STATUS Status;
+ UINT32 Len;
+
+ Packet = NULL;
+ DhcpSb = (DHCP_SERVICE *) Context;
+
+ //
+ // Don't restart receive if error occurs or DHCP is destoried.
+ //
+ if (EFI_ERROR (IoStatus)) {
+ return ;
+ } else if (DhcpSb->ServiceState == DHCP_DESTORY) {
+ NetbufFree (UdpPacket);
+ return ;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the packet received
+ //
+ if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
+ goto RESTART;
+ }
+
+ //
+ // Copy the DHCP message to a continuous memory block
+ //
+ Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
+ Packet = (EFI_DHCP4_PACKET *) NetAllocatePool (Len);
+
+ if (Packet == NULL) {
+ goto RESTART;
+ }
+
+ Packet->Size = Len;
+ Head = &Packet->Dhcp4.Header;
+ Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
+
+ if (Packet->Length != UdpPacket->TotalSize) {
+ goto RESTART;
+ }
+
+ //
+ // Is this packet the answer to our packet?
+ //
+ if ((Head->OpCode != BOOTP_REPLY) ||
+ (NTOHL (Head->Xid) != DhcpSb->Xid) ||
+ !NET_MAC_EQUAL (&DhcpSb->Mac, Head->ClientHwAddr, DhcpSb->HwLen)) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the options and retrieve the interested options
+ //
+ Para = NULL;
+ if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
+ (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
+ EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {
+
+ goto RESTART;
+ }
+
+ //
+ // Call the handler for each state. The handler should return
+ // EFI_SUCCESS if the process can go on no matter whether the
+ // packet is ignored or not. If the return is EFI_ERROR, the
+ // session will be terminated. Packet's ownership is handled
+ // over to the handlers. If operation succeeds, the handler
+ // must notify the user. It isn't necessary to do if EFI_ERROR
+ // is returned because the DhcpEndSession will notify the user.
+ //
+ Status = EFI_SUCCESS;
+
+ switch (DhcpSb->DhcpState) {
+ case Dhcp4Selecting:
+ Status = DhcpHandleSelect (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4Requesting:
+ Status = DhcpHandleRequest (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4InitReboot:
+ case Dhcp4Init:
+ case Dhcp4Bound:
+ //
+ // Ignore the packet in INITREBOOT, INIT and BOUND states
+ //
+ NetFreePool (Packet);
+ Status = EFI_SUCCESS;
+ break;
+
+ case Dhcp4Renewing:
+ case Dhcp4Rebinding:
+ Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4Rebooting:
+ Status = DhcpHandleReboot (DhcpSb, Packet, Para);
+ break;
+ }
+
+ if (Para != NULL) {
+ NetFreePool (Para);
+ }
+
+ Packet = NULL;
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (UdpPacket);
+ DhcpEndSession (DhcpSb, Status);
+ return ;
+ }
+
+RESTART:
+ NetbufFree (UdpPacket);
+
+ if (Packet != NULL) {
+ NetFreePool (Packet);
+ }
+
+ Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
+
+ if (EFI_ERROR (Status)) {
+ DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
+ }
+}
+
+
+/**
+ Release the packet.
+
+ @param Arg The packet to release
+
+ @return None
+
+**/
+VOID
+DhcpReleasePacket (
+ IN VOID *Arg
+ )
+{
+ NetFreePool (Arg);
+}
+
+
+/**
+ Release the net buffer when packet is sent.
+
+ @param UdpPacket The UDP packets received.
+ @param Points The local/remote UDP access points
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+ @return None
+
+**/
+VOID
+DhcpOnPacketSent (
+ NET_BUF *Packet,
+ UDP_POINTS *Points,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ NetbufFree (Packet);
+}
+
+
+
+/**
+ Build and transmit a DHCP message according to the current states.
+ This function implement the Table 5. of RFC 2131. Always transits
+ the state (as defined in Figure 5. of the same RFC) before sending
+ a DHCP message. The table is adjusted accordingly.
+
+ @param DhcpSb The DHCP service instance
+ @param Seed The seed packet which the new packet is based on
+ @param Para The DHCP parameter of the Seed packet
+ @param Type The message type to send
+ @param Msg The human readable message to include in the packet
+ sent.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
+ @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
+ @retval EFI_SUCCESS The message is sent
+
+**/
+EFI_STATUS
+DhcpSendMessage (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Seed,
+ IN DHCP_PARAMETER *Para,
+ IN UINT8 Type,
+ IN UINT8 *Msg
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ EFI_DHCP4_PACKET *Packet;
+ EFI_DHCP4_PACKET *NewPacket;
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *SeedHead;
+ UDP_IO_PORT *UdpIo;
+ UDP_POINTS EndPoint;
+ NET_BUF *Wrap;
+ NET_FRAGMENT Frag;
+ EFI_STATUS Status;
+ IP4_ADDR IpAddr;
+ UINT8 *Buf;
+ UINT16 MaxMsg;
+ UINT32 Len;
+ UINT32 Index;
+
+ //
+ // Allocate a big enough memory block to hold the DHCP packet
+ //
+ Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
+
+ if (Msg != NULL) {
+ Len += (UINT32)AsciiStrLen (Msg);
+ }
+
+ Packet = NetAllocatePool (Len);
+
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = Len;
+ Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
+
+ //
+ // Fill in the DHCP header fields
+ //
+ Config = &DhcpSb->ActiveConfig;
+ SeedHead = NULL;
+
+ if (Seed != NULL) {
+ SeedHead = &Seed->Dhcp4.Header;
+ }
+
+ Head = &Packet->Dhcp4.Header;
+ NetZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
+
+ Head->OpCode = BOOTP_REQUEST;
+ Head->HwType = DhcpSb->HwType;
+ Head->HwAddrLen = DhcpSb->HwLen;
+ Head->Xid = HTONL (DhcpSb->Xid);
+ Head->Reserved = HTONS (0x8000); //Server, broadcast the message please.
+
+ EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
+ NetCopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
+
+ //
+ // Append the DHCP message type
+ //
+ Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
+ Buf = Packet->Dhcp4.Option;
+ Buf = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type);
+
+ //
+ // Append the serverid option if necessary:
+ // 1. DHCP decline message
+ // 2. DHCP release message
+ // 3. DHCP request to confirm one lease.
+ //
+ if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
+ ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))) {
+
+ ASSERT ((Para != NULL) && (Para->ServerId != 0));
+
+ IpAddr = HTONL (Para->ServerId);
+ Buf = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);
+ }
+
+ //
+ // Append the requested IP option if necessary:
+ // 1. DHCP request to use the previously allocated address
+ // 2. DHCP request to confirm one lease
+ // 3. DHCP decline to decline one lease
+ //
+ IpAddr = 0;
+
+ if (Type == DHCP_MSG_REQUEST) {
+ if (DhcpSb->DhcpState == Dhcp4Rebooting) {
+ IpAddr = EFI_IP4 (Config->ClientAddress);
+
+ } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
+ ASSERT (SeedHead != NULL);
+ IpAddr = EFI_IP4 (SeedHead->YourAddr);
+ }
+
+ } else if (Type == DHCP_MSG_DECLINE) {
+ ASSERT (SeedHead != NULL);
+ IpAddr = EFI_IP4 (SeedHead->YourAddr);
+ }
+
+ if (IpAddr != 0) {
+ Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);
+ }
+
+ //
+ // Append the Max Message Length option if it isn't a DECLINE
+ // or RELEASE to direct the server use large messages instead of
+ // override the BOOTFILE and SERVER fields in the message head.
+ //
+ if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
+ MaxMsg = HTONS (0xFF00);
+ Buf = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);
+ }
+
+ //
+ // Append the user's message if it isn't NULL
+ //
+ if (Msg != NULL) {
+ Len = NET_MIN ((UINT32) AsciiStrLen (Msg), 255);
+ Buf = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg);
+ }
+
+ //
+ // Append the user configured options
+ //
+ if (DhcpSb->UserOptionLen != 0) {
+ for (Index = 0; Index < Config->OptionCount; Index++) {
+ //
+ // We can't use any option other than the client ID from user
+ // if it is a DHCP decline or DHCP release .
+ //
+ if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
+ (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) {
+ continue;
+ }
+
+ Buf = DhcpAppendOption (
+ Buf,
+ Config->OptionList[Index]->OpCode,
+ Config->OptionList[Index]->Length,
+ Config->OptionList[Index]->Data
+ );
+ }
+ }
+
+ *(Buf++) = DHCP_TAG_EOP;
+ Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);
+
+ //
+ // OK, the message is built, call the user to override it.
+ //
+ Status = EFI_SUCCESS;
+ NewPacket = NULL;
+
+ if (Type == DHCP_MSG_DISCOVER) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
+
+ } else if (Type == DHCP_MSG_REQUEST) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
+
+ } else if (Type == DHCP_MSG_DECLINE) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
+ }
+
+ if (EFI_ERROR (Status)) {
+ NetFreePool (Packet);
+ return Status;
+ }
+
+ if (NewPacket != NULL) {
+ NetFreePool (Packet);
+ Packet = NewPacket;
+ }
+
+ //
+ // Wrap it into a netbuf then send it.
+ //
+ Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;
+ Frag.Len = Packet->Length;
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);
+
+ if (Wrap == NULL) {
+ NetFreePool (Packet);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Save it as the last sent packet for retransmission
+ //
+ if (DhcpSb->LastPacket != NULL) {
+ NetbufFree (DhcpSb->LastPacket);
+ }
+
+ NET_GET_REF (Wrap);
+ DhcpSb->LastPacket = Wrap;
+ DhcpSetTransmitTimer (DhcpSb);
+
+ //
+ // Broadcast the message, unless we know the server address.
+ // Use the lease UdpIo port to send the unicast packet.
+ //
+ EndPoint.RemoteAddr = 0xffffffff;
+ EndPoint.LocalAddr = 0;
+ EndPoint.RemotePort = DHCP_SERVER_PORT;
+ EndPoint.LocalPort = DHCP_CLIENT_PORT;
+ UdpIo = DhcpSb->UdpIo;
+
+ if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
+ EndPoint.RemoteAddr = DhcpSb->ServerAddr;
+ EndPoint.LocalAddr = DhcpSb->ClientAddr;
+ UdpIo = DhcpSb->LeaseIoPort;
+ }
+
+ ASSERT (UdpIo != NULL);
+ Status = UdpIoSendDatagram (UdpIo, Wrap, &EndPoint, 0, DhcpOnPacketSent, DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Wrap);
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retransmit a saved packet. Only DISCOVER and REQUEST messages
+ will be retransmitted.
+
+ @param DhcpSb The DHCP service instance
+
+ @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
+ @retval EFI_SUCCESS The packet is retransmitted.
+
+**/
+EFI_STATUS
+DhcpRetransmit (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ UDP_IO_PORT *UdpIo;
+ UDP_POINTS EndPoint;
+ EFI_STATUS Status;
+
+ ASSERT (DhcpSb->LastPacket != NULL);
+
+ //
+ // Broadcast the message, unless we know the server address.
+ //
+ EndPoint.RemotePort = DHCP_SERVER_PORT;
+ EndPoint.LocalPort = DHCP_CLIENT_PORT;
+ EndPoint.RemoteAddr = 0xffffffff;
+ EndPoint.LocalAddr = 0;
+ UdpIo = DhcpSb->UdpIo;
+
+ if (DhcpSb->DhcpState == Dhcp4Renewing) {
+ EndPoint.RemoteAddr = DhcpSb->ServerAddr;
+ EndPoint.LocalAddr = DhcpSb->ClientAddr;
+ UdpIo = DhcpSb->LeaseIoPort;
+ }
+
+ ASSERT (UdpIo != NULL);
+
+ NET_GET_REF (DhcpSb->LastPacket);
+ Status = UdpIoSendDatagram (
+ UdpIo,
+ DhcpSb->LastPacket,
+ &EndPoint,
+ 0,
+ DhcpOnPacketSent,
+ DhcpSb
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (DhcpSb->LastPacket);
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Each DHCP service has three timer. Two of them are count down timer.
+ One for the packet retransmission. The other is to collect the offers.
+ The third timer increaments the lease life which is compared to T1, T2,
+ and lease to determine the time to renew and rebind the lease.
+ DhcpOnTimerTick will be called once every second.
+
+ @param Event The timer event
+ @param Context The context, which is the DHCP service instance.
+
+ @return None
+
+**/
+VOID
+EFIAPI
+DhcpOnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+
+ DhcpSb = (DHCP_SERVICE *) Context;
+
+ //
+ // Check the retransmit timer first
+ //
+ if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
+
+ if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
+ //
+ // Still has another try
+ //
+ DhcpRetransmit (DhcpSb);
+ DhcpSetTransmitTimer (DhcpSb);
+
+ } else {
+ if (!DHCP_CONNECTED (DhcpSb->DhcpState)) {
+ goto END_SESSION;
+ }
+
+ //
+ // Retransmission failed, if the DHCP request is initiated by
+ // user, adjust the current state according to the lease life.
+ // Otherwise do nothing to wait the lease to timeout
+ //
+ if (DhcpSb->ExtraRefresh) {
+ Status = EFI_SUCCESS;
+
+ if (DhcpSb->LeaseLife < DhcpSb->T1) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
+
+ } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
+
+ } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
+
+ } else {
+ goto END_SESSION;
+
+ }
+
+ DhcpSb->IoStatus = EFI_TIMEOUT;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
+ }
+ }
+ }
+
+ if ((DhcpSb->WaitOffer > 0) && (--DhcpSb->WaitOffer == 0)) {
+ //
+ // OK, offer collection finished, select a offer
+ //
+ ASSERT (DhcpSb->DhcpState == Dhcp4Selecting);
+
+ if (DhcpSb->LastOffer == NULL) {
+ goto END_SESSION;
+ }
+
+ if (EFI_ERROR (DhcpChooseOffer (DhcpSb))) {
+ goto END_SESSION;
+ }
+ }
+
+ //
+ // If an address has been acquired, check whether need to
+ // refresh or whether it has expired.
+ //
+ if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
+ DhcpSb->LeaseLife++;
+
+ //
+ // Don't timeout the lease, only count the life if user is
+ // requesting extra renew/rebind. Adjust the state after that.
+ //
+ if (DhcpSb->ExtraRefresh) {
+ return ;
+ }
+
+ if (DhcpSb->LeaseLife == DhcpSb->Lease) {
+ //
+ // Lease expires, end the session
+ //
+ goto END_SESSION;
+
+ } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
+ //
+ // T2 expires, transit to rebinding then send a REQUEST to any server
+ //
+ if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
+ goto END_SESSION;
+ }
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto END_SESSION;
+ }
+
+ } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
+ //
+ // T1 expires, transit to renewing, then send a REQUEST to the server
+ //
+ if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
+ goto END_SESSION;
+ }
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto END_SESSION;
+ }
+ }
+ }
+
+ return ;
+
+END_SESSION:
+ DhcpEndSession (DhcpSb, EFI_TIMEOUT);
+
+ return ;
+}
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h
new file mode 100644
index 0000000000..1da95f94a7
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h
@@ -0,0 +1,115 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Dhcp4Io.h
+
+Abstract:
+
+ The DHCP4 protocol implementation.
+
+
+**/
+
+#ifndef __EFI_DHCP4_IO_H__
+#define __EFI_DHCP4_IO_H__
+
+#include <PiDxe.h>
+
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/NetLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+
+enum {
+ DHCP_WAIT_OFFER = 3, // Time to wait the offers
+ DHCP_DEFAULT_LEASE = 7 *24 *60 *60, // Seven days as default.
+ DHCP_SERVER_PORT = 67,
+ DHCP_CLIENT_PORT = 68,
+
+ //
+ // BOOTP header "op" field
+ //
+ BOOTP_REQUEST = 1,
+ BOOTP_REPLY = 2,
+
+ //
+ // DHCP message types
+ //
+ DHCP_MSG_DISCOVER = 1,
+ DHCP_MSG_OFFER = 2,
+ DHCP_MSG_REQUEST = 3,
+ DHCP_MSG_DECLINE = 4,
+ DHCP_MSG_ACK = 5,
+ DHCP_MSG_NAK = 6,
+ DHCP_MSG_RELEASE = 7,
+ DHCP_MSG_INFORM = 8,
+
+ //
+ // DHCP notify user type
+ //
+ DHCP_NOTIFY_COMPLETION = 1,
+ DHCP_NOTIFY_RENEWREBIND,
+ DHCP_NOTIFY_ALL,
+};
+
+#define DHCP_IS_BOOTP(Parameter) (((Parameter) == NULL) || ((Parameter)->DhcpType == 0))
+
+#define DHCP_CONNECTED(State) \
+ (((State) == Dhcp4Bound) || ((State) == (Dhcp4Renewing)) || ((State) == Dhcp4Rebinding))
+
+EFI_STATUS
+DhcpSetState (
+ IN DHCP_SERVICE *DhcpSb,
+ IN INTN State,
+ IN BOOLEAN CallUser
+ );
+
+EFI_STATUS
+DhcpSendMessage (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Seed,
+ IN DHCP_PARAMETER *Para,
+ IN UINT8 Type,
+ IN UINT8 *Msg
+ );
+
+VOID
+EFIAPI
+DhcpOnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+VOID
+DhcpInput (
+ NET_BUF *UdpPacket,
+ UDP_POINTS *Points,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ );
+
+EFI_STATUS
+DhcpInitRequest (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+VOID
+DhcpCleanLease (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c
new file mode 100644
index 0000000000..b16f4693d7
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c
@@ -0,0 +1,906 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Dhcp4Option.c
+
+Abstract:
+
+ Function to validate, parse, process the DHCP options
+
+
+**/
+
+#include "Dhcp4Impl.h"
+
+//
+// A list of the format of DHCP Options sorted by option tag
+// to validate a dhcp message. Refere the comments of the
+// DHCP_OPTION_FORMAT structure.
+//
+STATIC
+DHCP_OPTION_FORMAT
+DhcpOptionFormats [] = {
+ {DHCP_TAG_NETMASK, DHCP_OPTION_IP, 1, 1 , TRUE},
+ {DHCP_TAG_TIME_OFFSET, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP_TAG_ROUTER, DHCP_OPTION_IP, 1, -1 , TRUE},
+ {DHCP_TAG_TIME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NAME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_DNS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_LOG_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_COOKIE_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_LPR_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_IMPRESS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_RL_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_HOSTNAME, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_BOOTFILE_LEN, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP_TAG_DUMP, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_DOMAINNAME, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_SWAP_SERVER, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP_TAG_ROOTPATH, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_EXTEND_PATH, DHCP_OPTION_INT8, 1, -1 , FALSE},
+
+ {DHCP_TAG_IPFORWARD, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_NONLOCAL_SRR, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_POLICY_SRR, DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
+ {DHCP_TAG_EMTU, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP_TAG_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP_TAG_PATHMTU_AGE, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16, 1, -1 , FALSE},
+
+ {DHCP_TAG_IFMTU, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP_TAG_SUBNET_LOCAL, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_BROADCAST, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP_TAG_DISCOVER_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_SUPPLY_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP_TAG_STATIC_ROUTE, DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
+
+ {DHCP_TAG_TRAILER, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP_TAG_ARPAGE, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP_TAG_ETHER_ENCAP, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+
+ {DHCP_TAG_TCP_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP_TAG_KEEP_INTERVAL, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP_TAG_KEEP_GARBAGE, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+
+ {DHCP_TAG_NIS_DOMAIN, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_NIS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NTP_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_VENDOR, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_NBNS, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NBDD, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NBTYPE, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP_TAG_NBSCOPE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_XFONT, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_XDM, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP_TAG_REQUEST_IP, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP_TAG_LEASE, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP_TAG_OVERLOAD, DHCP_OPTION_INT8, 1, 1 , TRUE},
+ {DHCP_TAG_TYPE, DHCP_OPTION_INT8, 1, 1 , TRUE},
+ {DHCP_TAG_SERVER_ID, DHCP_OPTION_IP, 1, 1 , TRUE},
+ {DHCP_TAG_PARA_LIST, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_MESSAGE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_MAXMSG, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP_TAG_T1, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP_TAG_T2, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP_TAG_VENDOR_CLASS, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_CLIENT_ID, DHCP_OPTION_INT8, 2, -1 , FALSE},
+
+ {DHCP_TAG_NISPLUS, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_NISPLUS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP_TAG_TFTP, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP_TAG_BOOTFILE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+
+ {DHCP_TAG_MOBILEIP, DHCP_OPTION_IP, 0, -1 , FALSE},
+ {DHCP_TAG_SMTP, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_POP3, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_NNTP, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_WWW, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_FINGER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_IRC, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_STTALK, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP_TAG_STDA, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP_TAG_CLASSLESS_ROUTE,DHCP_OPTION_INT8, 5, -1 , FALSE},
+};
+
+
+/**
+ Binary search the DhcpOptionFormats array to find the format
+ information about a specific option.
+
+ @param Tag The option's tag.
+
+ @return The point to the option's format, NULL if not found.
+
+**/
+STATIC
+DHCP_OPTION_FORMAT *
+DhcpFindOptionFormat (
+ IN UINT8 Tag
+ )
+{
+ INTN Left;
+ INTN Right;
+ INTN Middle;
+
+ Left = 0;
+ Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1;
+
+ while (Right >= Left) {
+ Middle = (Left + Right) / 2;
+
+ if (Tag == DhcpOptionFormats[Middle].Tag) {
+ return &DhcpOptionFormats[Middle];
+ }
+
+ if (Tag < DhcpOptionFormats[Middle].Tag) {
+ Right = Middle - 1;
+ } else {
+ Left = Middle + 1;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Validate whether a single DHCP option is valid according to its format.
+
+ @param Format The option's format
+ @param OptValue The value of the option
+ @param Len The length of the option value
+
+ @return TRUE is the option is valid, otherwise FALSE.
+
+**/
+STATIC
+BOOLEAN
+DhcpOptionIsValid (
+ IN DHCP_OPTION_FORMAT *Format,
+ IN UINT8 *OptValue,
+ IN INTN Len
+ )
+{
+ INTN Unit;
+ INTN Occur;
+ INTN Index;
+
+ Unit = 0;
+
+ switch (Format->Type) {
+ case DHCP_OPTION_SWITCH:
+ case DHCP_OPTION_INT8:
+ Unit = 1;
+ break;
+
+ case DHCP_OPTION_INT16:
+ Unit = 2;
+ break;
+
+ case DHCP_OPTION_INT32:
+ case DHCP_OPTION_IP:
+ Unit = 4;
+ break;
+
+ case DHCP_OPTION_IPPAIR:
+ Unit = 8;
+ break;
+ }
+
+ ASSERT (Unit != 0);
+
+ //
+ // Validate that the option appears in the full units.
+ //
+ if ((Len % Unit) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Validate the occurance of the option unit is with in [MinOccur, MaxOccur]
+ //
+ Occur = Len / Unit;
+
+ if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) ||
+ ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))) {
+ return FALSE;
+ }
+
+ //
+ // If the option is of type switch, only 0/1 are valid values.
+ //
+ if (Format->Type == DHCP_OPTION_SWITCH) {
+ for (Index = 0; Index < Occur; Index++) {
+ if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Extract the client interested options, all the parameters are
+ converted to host byte order.
+
+ @param Tag The DHCP option tag
+ @param Len The length of the option
+ @param Data The value of the DHCP option
+ @param Para The variable to save the interested parameter
+
+ @retval EFI_SUCCESS The DHCP option is successfully extracted.
+ @retval EFI_INVALID_PARAMETER The DHCP option is mal-formated
+
+**/
+STATIC
+EFI_STATUS
+DhcpGetParameter (
+ IN UINT8 Tag,
+ IN INTN Len,
+ IN UINT8 *Data,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ switch (Tag) {
+ case DHCP_TAG_NETMASK:
+ Para->NetMask = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_ROUTER:
+ //
+ // Return the first router to consumer which is the preferred one
+ //
+ Para->Router = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_LEASE:
+ Para->Lease = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_OVERLOAD:
+ Para->Overload = *Data;
+
+ if ((Para->Overload < 1) || (Para->Overload > 3)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case DHCP_TAG_TYPE:
+ Para->DhcpType = *Data;
+
+ if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case DHCP_TAG_SERVER_ID:
+ Para->ServerId = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_T1:
+ Para->T1 = NetGetUint32 (Data);
+ break;
+
+ case DHCP_TAG_T2:
+ Para->T2 = NetGetUint32 (Data);
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Inspect all the options in a single buffer. DHCP options may be contained
+ in several buffers, such as the BOOTP options filed, boot file or server
+ name. Each option buffer is required to end with DHCP_TAG_EOP.
+
+ @param Buffer The buffer which contains DHCP options
+ @param BufLen The length of the buffer
+ @param Check The callback function for each option found
+ @param Context The opaque parameter for the Check
+ @param Overload variable to save the value of DHCP_TAG_OVERLOAD
+ option.
+
+ @retval EFI_SUCCESS All the options are valid
+ @retval EFI_INVALID_PARAMETER The options are mal-formated.
+
+**/
+STATIC
+EFI_STATUS
+DhcpIterateBufferOptions (
+ IN UINT8 *Buffer,
+ IN INTN BufLen,
+ IN DHCP_CHECK_OPTION Check, OPTIONAL
+ IN VOID *Context,
+ OUT UINT8 *Overload OPTIONAL
+ )
+{
+ INTN Cur;
+ UINT8 Tag;
+ UINT8 Len;
+
+ Cur = 0;
+
+ while (Cur < BufLen) {
+ Tag = Buffer[Cur];
+
+ if (Tag == DHCP_TAG_PAD) {
+ Cur++;
+ continue;
+ } else if (Tag == DHCP_TAG_EOP) {
+ return EFI_SUCCESS;
+ }
+
+ Cur++;
+
+ if (Cur == BufLen) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Len = Buffer[Cur++];
+
+ if (Cur + Len > BufLen) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Tag == DHCP_TAG_OVERLOAD) && (Overload != NULL)) {
+ if (Len != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Overload = Buffer[Cur];
+ }
+
+ if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cur += Len;
+ }
+
+ //
+ // Each option buffer is expected to end with an EOP
+ //
+ return EFI_INVALID_PARAMETER;
+}
+
+
+/**
+ Iterate through a DHCP message to visit each option. First inspect
+ all the options in the OPTION field. Then if overloaded, inspect
+ the options in FILENAME and SERVERNAME fields. One option may be
+ encoded in several places. See RFC 3396 Encoding Long Options in DHCP
+
+ @param Packet The DHCP packet to check the options for
+ @param Check The callback function to be called for each option
+ found
+ @param Context The opaque parameter for Check
+
+ @retval EFI_SUCCESS The DHCP packet's options are well formated
+ @retval Others The DHCP packet's options are not well formated
+
+**/
+EFI_STATUS
+DhcpIterateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_CHECK_OPTION Check, OPTIONAL
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Overload;
+
+ Overload = 0;
+
+ Status = DhcpIterateBufferOptions (
+ Packet->Dhcp4.Option,
+ Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32),
+ Check,
+ Context,
+ &Overload
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
+ Status = DhcpIterateBufferOptions (
+ Packet->Dhcp4.Header.BootFileName,
+ 128,
+ Check,
+ Context,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
+ Status = DhcpIterateBufferOptions (
+ Packet->Dhcp4.Header.ServerName,
+ 64,
+ Check,
+ Context,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call back function to DhcpiterateOptions to compute each option's
+ length. It just adds the data length of all the occurances of this
+ Tag. Context is an array of 256 DHCP_OPTION_COUNT.
+
+ @param Tag The current option to check
+ @param Len The length of the option data
+ @param Data The option data
+ @param Context The context, which is a array of 256
+ DHCP_OPTION_COUNT.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS.
+
+**/
+STATIC
+EFI_STATUS
+DhcpGetOptionLen (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_OPTION_COUNT *OpCount;
+
+ OpCount = (DHCP_OPTION_COUNT *) Context;
+ OpCount[Tag].Offset = OpCount[Tag].Offset + Len;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call back function to DhcpiterateOptions to consolidate each option's
+ data. There are maybe several occurance of the same option.
+
+ @param Tag The option to consolidate its data
+ @param Len The length of option data
+ @param Data The data of the option's current occurance
+ @param Context The context, which is DHCP_OPTION_CONTEXT. This
+ array is just a wrap to pass THREE parameters.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS
+
+**/
+STATIC
+EFI_STATUS
+DhcpFillOption (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_OPTION_CONTEXT *OptContext;
+ DHCP_OPTION_COUNT *OptCount;
+ DHCP_OPTION *Options;
+ UINT8 *Buf;
+ UINT8 Index;
+
+ OptContext = (DHCP_OPTION_CONTEXT *) Context;
+
+ OptCount = OptContext->OpCount;
+ Index = OptCount[Tag].Index;
+ Options = OptContext->Options;
+ Buf = OptContext->Buf;
+
+ if (Options[Index].Data == NULL) {
+ Options[Index].Tag = Tag;
+ Options[Index].Data = Buf + OptCount[Tag].Offset;
+ }
+
+ NetCopyMem (Buf + OptCount[Tag].Offset, Data, Len);
+
+ OptCount[Tag].Offset = OptCount[Tag].Offset + Len;
+ Options[Index].Len = Options[Index].Len + Len;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the options of a DHCP packet. It supports RFC 3396: Encoding
+ Long Options in DHCP. That is, it will combine all the option value
+ of all the occurances of each option.
+ A little bit of implemenation:
+ It adopts the "Key indexed counting" algorithm. First, it allocates
+ an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded
+ as a UINT8. It then iterates the DHCP packet to get data length of
+ each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it
+ knows the number of present options and their length. It allocates a
+ array of DHCP_OPTION and a continous buffer after the array to put
+ all the options' data. Each option's data is pointed to by the Data
+ field in DHCP_OPTION structure. At last, it call DhcpIterateOptions
+ with DhcpFillOption to fill each option's data to its position in the
+ buffer.
+
+ @param Packet The DHCP packet to parse the options
+ @param Count The number of valid dhcp options present in the
+ packet
+ @param OptionPoint The array that contains the DHCP options. Caller
+ should free it.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to parse the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formated
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpParseOption (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT INTN *Count,
+ OUT DHCP_OPTION **OptionPoint
+ )
+{
+ DHCP_OPTION_CONTEXT Context;
+ DHCP_OPTION *Options;
+ DHCP_OPTION_COUNT *OptCount;
+ EFI_STATUS Status;
+ UINT16 TotalLen;
+ INTN OptNum;
+ INTN Index;
+
+ ASSERT ((Count != NULL) && (OptionPoint != NULL));
+
+ //
+ // First compute how many options and how long each option is
+ // with the "Key indexed counting" algorithms.
+ //
+ OptCount = NetAllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));
+
+ if (OptCount == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Before the loop, Offset is the length of the option. After loop,
+ // OptCount[Index].Offset specifies the offset into the continuous
+ // option value buffer to put the data.
+ //
+ TotalLen = 0;
+ OptNum = 0;
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (OptCount[Index].Offset != 0) {
+ OptCount[Index].Index = (UINT8) OptNum;
+
+ TotalLen = TotalLen + OptCount[Index].Offset;
+ OptCount[Index].Offset = TotalLen - OptCount[Index].Offset;
+
+ OptNum++;
+ }
+ }
+
+ *Count = OptNum;
+ *OptionPoint = NULL;
+
+ if (OptNum == 0) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Allocate a buffer to hold the DHCP options, and after that, a
+ // continuous buffer to put all the options' data.
+ //
+ Options = NetAllocateZeroPool (OptNum * sizeof (DHCP_OPTION) + TotalLen);
+
+ if (Options == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Context.OpCount = OptCount;
+ Context.Options = Options;
+ Context.Buf = (UINT8 *) (Options + OptNum);
+
+ Status = DhcpIterateOptions (Packet, DhcpFillOption, &Context);
+
+ if (EFI_ERROR (Status)) {
+ NetFreePool (Options);
+ goto ON_EXIT;
+ }
+
+ *OptionPoint = Options;
+
+ON_EXIT:
+ NetFreePool (OptCount);
+ return Status;
+}
+
+
+/**
+ Validate the packet's options. If necessary, allocate
+ and fill in the interested parameters.
+
+ @param Packet The packet to validate the options
+ @param Para The variable to save the DHCP parameters.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to validate the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formated
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpValidateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT DHCP_PARAMETER **Para OPTIONAL
+ )
+{
+ DHCP_PARAMETER Parameter;
+ DHCP_OPTION_FORMAT *Format;
+ DHCP_OPTION *AllOption;
+ DHCP_OPTION *Option;
+ EFI_STATUS Status;
+ BOOLEAN Updated;
+ INTN Count;
+ INTN Index;
+
+ if (Para != NULL) {
+ *Para = NULL;
+ }
+
+ AllOption = NULL;
+ Status = DhcpParseOption (Packet, &Count, &AllOption);
+
+ if (EFI_ERROR (Status) || (Count == 0)) {
+ return Status;
+ }
+
+ Updated = FALSE;
+ NetZeroMem (&Parameter, sizeof (Parameter));
+
+ for (Index = 0; Index < Count; Index++) {
+ Option = &AllOption[Index];
+
+ //
+ // Find the format of the option then validate it.
+ //
+ Format = DhcpFindOptionFormat (Option->Tag);
+
+ if (Format == NULL) {
+ continue;
+ }
+
+ if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ //
+ // Get the client interested parameters
+ //
+ if (Format->Alert && (Para != NULL)) {
+ Updated = TRUE;
+ Status = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ if (Updated && (Para != NULL)) {
+ if ((*Para = NetAllocatePool (sizeof (DHCP_PARAMETER))) == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ CopyMem (*Para, &Parameter, sizeof (DHCP_PARAMETER));
+ }
+
+ON_EXIT:
+ NetFreePool (AllOption);
+ return Status;
+}
+
+
+
+/**
+ Append an option to the memory, if the option is longer than
+ 255 bytes, splits it into several options.
+
+ @param Buf The buffer to append the option to
+ @param Tag The option's tag
+ @param DataLen The length of the option's data
+ @param Data The option's data
+
+ @return The position to append the next option
+
+**/
+UINT8 *
+DhcpAppendOption (
+ IN UINT8 *Buf,
+ IN UINT8 Tag,
+ IN UINT16 DataLen,
+ IN UINT8 *Data
+ )
+{
+ INTN Index;
+ INTN Len;
+
+ ASSERT (DataLen != 0);
+
+ for (Index = 0; Index < (DataLen + 254) / 255; Index++) {
+ Len = NET_MIN (255, DataLen - Index * 255);
+
+ *(Buf++) = Tag;
+ *(Buf++) = (UINT8) Len;
+ NetCopyMem (Buf, Data + Index * 255, Len);
+
+ Buf += Len;
+ }
+
+ return Buf;
+}
+
+
+/**
+ Build a new DHCP packet from a seed packet. Options may be deleted or
+ appended. The caller should free the NewPacket when finished using it.
+
+ @param SeedPacket The seed packet to start with
+ @param DeleteCount The number of options to delete
+ @param DeleteList The options to delete from the packet
+ @param AppendCount The number of options to append
+ @param AppendList The options to append to the packet
+ @param NewPacket The new packet, allocated and built by this
+ function.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval EFI_SUCCESS The packet is build.
+
+**/
+EFI_STATUS
+DhcpBuild (
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ )
+{
+ DHCP_OPTION *Mark;
+ DHCP_OPTION *SeedOptions;
+ EFI_DHCP4_PACKET *Packet;
+ EFI_STATUS Status;
+ INTN Count;
+ UINT32 Index;
+ UINT32 Len;
+ UINT8 *Buf;
+
+ //
+ // Use an array of DHCP_OPTION to mark the existance
+ // and position of each valid options.
+ //
+ Mark = NetAllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);
+
+ if (Mark == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ Mark[Index].Tag = (UINT8) Index;
+ Mark[Index].Len = 0;
+ }
+
+ //
+ // Get list of the options from the seed packet, then put
+ // them to the mark array according to their tags.
+ //
+ SeedOptions = NULL;
+ Status = DhcpParseOption (SeedPacket, &Count, &SeedOptions);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ for (Index = 0; Index < (UINT32) Count; Index++) {
+ Mark[SeedOptions[Index].Tag] = SeedOptions[Index];
+ }
+
+ //
+ // Mark the option's length is zero if it is in the DeleteList.
+ //
+ for (Index = 0; Index < DeleteCount; Index++) {
+ Mark[DeleteList[Index]].Len = 0;
+ }
+
+ //
+ // Add or replace the option if it is in the append list.
+ //
+ for (Index = 0; Index < AppendCount; Index++) {
+ Mark[AppendList[Index]->OpCode].Len = AppendList[Index]->Length;
+ Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data;
+ }
+
+ //
+ // compute the new packet length. No need to add 1 byte for
+ // EOP option since EFI_DHCP4_PACKET includes one extra byte
+ // for option. It is necessary to split the option if it is
+ // longer than 255 bytes.
+ //
+ Len = sizeof (EFI_DHCP4_PACKET);
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (Mark[Index].Len != 0) {
+ Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len;
+ }
+ }
+
+ Status = EFI_OUT_OF_RESOURCES;
+ Packet = (EFI_DHCP4_PACKET *) NetAllocatePool (Len);
+
+ if (Packet == NULL) {
+ goto ON_ERROR;
+ }
+
+ Packet->Size = Len;
+ Packet->Length = 0;
+ CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (EFI_DHCP4_HEADER));
+ Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
+ Buf = Packet->Dhcp4.Option;
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (Mark[Index].Len != 0) {
+ Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data);
+ }
+ }
+
+ *(Buf++) = DHCP_TAG_EOP;
+ Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)
+ + (UINT32) (Buf - Packet->Dhcp4.Option);
+
+ *NewPacket = Packet;
+ Status = EFI_SUCCESS;
+
+ON_ERROR:
+ if (SeedOptions != NULL) {
+ NetFreePool (SeedOptions);
+ }
+
+ NetFreePool (Mark);
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h
new file mode 100644
index 0000000000..a6a485bece
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h
@@ -0,0 +1,266 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Dhcp4Option.h
+
+Abstract:
+
+ To validate, parse and process the DHCP options
+
+
+**/
+
+#ifndef __EFI_DHCP4_OPTION_H__
+#define __EFI_DHCP4_OPTION_H__
+
+//
+// DHCP option tags (types)
+//
+enum {
+ //
+ // RFC1497 vendor extensions
+ //
+ DHCP_TAG_PAD = 0, // Pad Option
+ DHCP_TAG_EOP = 255, // End Option
+ DHCP_TAG_NETMASK = 1, // Subnet Mask
+ DHCP_TAG_TIME_OFFSET = 2, // Time Offset from UTC
+ DHCP_TAG_ROUTER = 3, // Router option,
+ DHCP_TAG_TIME_SERVER = 4, // Time Server
+ DHCP_TAG_NAME_SERVER = 5, // Name Server
+ DHCP_TAG_DNS_SERVER = 6, // Domain Name Server
+ DHCP_TAG_LOG_SERVER = 7, // Log Server
+ DHCP_TAG_COOKIE_SERVER = 8, // Cookie Server
+ DHCP_TAG_LPR_SERVER = 9, // LPR Print Server
+ DHCP_TAG_IMPRESS_SERVER = 10, // Impress Server
+ DHCP_TAG_RL_SERVER = 11, // Resource Location Server
+ DHCP_TAG_HOSTNAME = 12, // Host Name
+ DHCP_TAG_BOOTFILE_LEN = 13, // Boot File Size
+ DHCP_TAG_DUMP = 14, // Merit Dump File
+ DHCP_TAG_DOMAINNAME = 15, // Domain Name
+ DHCP_TAG_SWAP_SERVER = 16, // Swap Server
+ DHCP_TAG_ROOTPATH = 17, // Root path
+ DHCP_TAG_EXTEND_PATH = 18, // Extensions Path
+
+ //
+ // IP Layer Parameters per Host
+ //
+ DHCP_TAG_IPFORWARD = 19, // IP Forwarding Enable/Disable
+ DHCP_TAG_NONLOCAL_SRR = 20, // on-Local Source Routing Enable/Disable
+ DHCP_TAG_POLICY_SRR = 21, // Policy Filter
+ DHCP_TAG_EMTU = 22, // Maximum Datagram Reassembly Size
+ DHCP_TAG_TTL = 23, // Default IP Time-to-live
+ DHCP_TAG_PATHMTU_AGE = 24, // Path MTU Aging Timeout
+ DHCP_TAG_PATHMTU_PLATEAU = 25, // Path MTU Plateau Table
+
+ //
+ // IP Layer Parameters per Interface
+ //
+ DHCP_TAG_IFMTU = 26, // Interface MTU
+ DHCP_TAG_SUBNET_LOCAL = 27, // All Subnets are Local
+ DHCP_TAG_BROADCAST = 28, // Broadcast Address
+ DHCP_TAG_DISCOVER_MASK = 29, // Perform Mask Discovery
+ DHCP_TAG_SUPPLY_MASK = 30, // Mask Supplier
+ DHCP_TAG_DISCOVER_ROUTE = 31, // Perform Router Discovery
+ DHCP_TAG_ROUTER_SOLICIT = 32, // Router Solicitation Address
+ DHCP_TAG_STATIC_ROUTE = 33, // Static Route
+
+ //
+ // Link Layer Parameters per Interface
+ //
+ DHCP_TAG_TRAILER = 34, // Trailer Encapsulation
+ DHCP_TAG_ARPAGE = 35, // ARP Cache Timeout
+ DHCP_TAG_ETHER_ENCAP = 36, // Ethernet Encapsulation
+
+ //
+ // TCP Parameters
+ //
+ DHCP_TAG_TCP_TTL = 37, // TCP Default TTL
+ DHCP_TAG_KEEP_INTERVAL = 38, // TCP Keepalive Interval
+ DHCP_TAG_KEEP_GARBAGE = 39, // TCP Keepalive Garbage
+
+ //
+ // Application and Service Parameters
+ //
+ DHCP_TAG_NIS_DOMAIN = 40, // Network Information Service Domain
+ DHCP_TAG_NIS_SERVER = 41, // Network Information Servers
+ DHCP_TAG_NTP_SERVER = 42, // Network Time Protocol Servers
+ DHCP_TAG_VENDOR = 43, // Vendor Specific Information
+ DHCP_TAG_NBNS = 44, // NetBIOS over TCP/IP Name Server
+ DHCP_TAG_NBDD = 45, // NetBIOS Datagram Distribution Server
+ DHCP_TAG_NBTYPE = 46, // NetBIOS over TCP/IP Node Type
+ DHCP_TAG_NBSCOPE = 47, // NetBIOS over TCP/IP Scope
+ DHCP_TAG_XFONT = 48, // X Window System Font Server
+ DHCP_TAG_XDM = 49, // X Window System Display Manager
+ DHCP_TAG_NISPLUS = 64, // Network Information Service+ Domain
+ DHCP_TAG_NISPLUS_SERVER = 65, // Network Information Service+ Servers
+ DHCP_TAG_MOBILEIP = 68, // Mobile IP Home Agent
+ DHCP_TAG_SMTP = 69, // Simple Mail Transport Protocol Server
+ DHCP_TAG_POP3 = 70, // Post Office Protocol (POP3) Server
+ DHCP_TAG_NNTP = 71, // Network News Transport Protocol Server
+ DHCP_TAG_WWW = 72, // Default World Wide Web (WWW) Server
+ DHCP_TAG_FINGER = 73, // Default Finger Server
+ DHCP_TAG_IRC = 74, // Default Internet Relay Chat (IRC) Server
+ DHCP_TAG_STTALK = 75, // StreetTalk Server
+ DHCP_TAG_STDA = 76, // StreetTalk Directory Assistance Server
+ DHCP_TAG_CLASSLESS_ROUTE = 121, // Classless Route
+
+ //
+ // DHCP Extensions
+ //
+ DHCP_TAG_REQUEST_IP = 50, // Requested IP Address
+ DHCP_TAG_LEASE = 51, // IP Address Lease Time
+ DHCP_TAG_OVERLOAD = 52, // Option Overload
+ DHCP_TAG_TFTP = 66, // TFTP server name
+ DHCP_TAG_BOOTFILE = 67, // Bootfile name
+ DHCP_TAG_TYPE = 53, // DHCP Message Type
+ DHCP_TAG_SERVER_ID = 54, // Server Identifier
+ DHCP_TAG_PARA_LIST = 55, // Parameter Request List
+ DHCP_TAG_MESSAGE = 56, // Message
+ DHCP_TAG_MAXMSG = 57, // Maximum DHCP Message Size
+ DHCP_TAG_T1 = 58, // Renewal (T1) Time Value
+ DHCP_TAG_T2 = 59, // Rebinding (T2) Time Value
+ DHCP_TAG_VENDOR_CLASS = 60, // Vendor class identifier
+ DHCP_TAG_CLIENT_ID = 61, // Client-identifier
+};
+
+enum {
+ DHCP_OPTION_MAGIC = 0x63538263, // Network byte order
+ DHCP_MAX_OPTIONS = 256,
+
+ //
+ // DHCP option types, this is used to validate the DHCP options.
+ //
+ DHCP_OPTION_SWITCH = 1,
+ DHCP_OPTION_INT8,
+ DHCP_OPTION_INT16,
+ DHCP_OPTION_INT32,
+ DHCP_OPTION_IP,
+ DHCP_OPTION_IPPAIR,
+
+ //
+ // Value of DHCP overload option
+ //
+ DHCP_OVERLOAD_FILENAME = 1,
+ DHCP_OVERLOAD_SVRNAME = 2,
+ DHCP_OVERLOAD_BOTH = 3,
+};
+
+//
+// The DHCP option structure. This structure extends the EFI_DHCP_OPTION
+// structure to support options longer than 255 bytes, such as classless route.
+//
+typedef struct {
+ UINT8 Tag;
+ UINT16 Len;
+ UINT8 *Data;
+} DHCP_OPTION;
+
+//
+// Structures used to parse the DHCP options with RFC3396 support.
+//
+typedef struct {
+ UINT8 Index;
+ UINT16 Offset;
+} DHCP_OPTION_COUNT;
+
+typedef struct {
+ DHCP_OPTION_COUNT *OpCount;
+ DHCP_OPTION *Options;
+ UINT8 *Buf;
+} DHCP_OPTION_CONTEXT;
+
+//
+// The options that matters to DHCP driver itself. The user of
+// DHCP clients may be interested in other options, such as
+// classless route, who can parse the DHCP offer to get them.
+//
+typedef struct {
+ IP4_ADDR NetMask; // DHCP_TAG_NETMASK
+ IP4_ADDR Router; // DHCP_TAG_ROUTER, only the first router is used
+
+ //
+ // DHCP specific options
+ //
+ UINT8 DhcpType; // DHCP_TAG_TYPE
+ UINT8 Overload; // DHCP_TAG_OVERLOAD
+ IP4_ADDR ServerId; // DHCP_TAG_SERVER_ID
+ UINT32 Lease; // DHCP_TAG_LEASE
+ UINT32 T1; // DHCP_TAG_T1
+ UINT32 T2; // DHCP_TAG_T2
+} DHCP_PARAMETER;
+
+//
+// Structure used to describe and validate the format of DHCP options.
+// Type is the options' data type, such as DHCP_OPTION_INT8. MinOccur
+// is the minium occurance of this data type. MaxOccur is defined
+// similarly. If MaxOccur is -1, it means that there is no limit on the
+// maximum occurance. Alert tells whether DHCP client should further
+// inspect the option to parse DHCP_PARAMETER.
+//
+typedef struct {
+ UINT8 Tag;
+ INTN Type;
+ INTN MinOccur;
+ INTN MaxOccur;
+ BOOLEAN Alert;
+} DHCP_OPTION_FORMAT;
+
+typedef
+EFI_STATUS
+(*DHCP_CHECK_OPTION) (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ );
+
+EFI_STATUS
+DhcpIterateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_CHECK_OPTION Check, OPTIONAL
+ IN VOID *Context
+ );
+
+EFI_STATUS
+DhcpValidateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT DHCP_PARAMETER **Para OPTIONAL
+ );
+
+EFI_STATUS
+DhcpParseOption (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT INTN *Count,
+ OUT DHCP_OPTION **OptionPoint
+ );
+
+UINT8 *
+DhcpAppendOption (
+ IN UINT8 *Buf,
+ IN UINT8 Tag,
+ IN UINT16 DataLen,
+ IN UINT8 *Data
+ );
+
+EFI_STATUS
+DhcpBuild (
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c b/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c
index 326fb4dc33..a1bae0f29d 100644
--- a/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c
+++ b/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c
@@ -590,6 +590,8 @@ Ip4ConfigOnDhcp4Complete (
EFI_STATUS Status;
BOOLEAN Perment;
IP4_ADDR Subnet;
+ IP4_ADDR Ip1;
+ IP4_ADDR Ip2;
Instance = (IP4_CONFIG_INSTANCE *) Context;
ASSERT (Instance->Dhcp4 != NULL);
@@ -641,20 +643,24 @@ Ip4ConfigOnDhcp4Complete (
//
Ip4Config->RouteTableSize = 1;
- Subnet = EFI_NTOHL (Dhcp4Mode.ClientAddress) & EFI_NTOHL (Dhcp4Mode.SubnetMask);
+ NetCopyMem (&Ip1, &Dhcp4Mode.ClientAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&Ip2, &Dhcp4Mode.SubnetMask, sizeof (IP4_ADDR));
+
+ Subnet = Ip1 & Ip2;
- EFI_IP4 (Ip4Config->RouteTable[0].SubnetAddress) = HTONL (Subnet);
- Ip4Config->RouteTable[0].SubnetMask = Dhcp4Mode.SubnetMask;
- EFI_IP4 (Ip4Config->RouteTable[0].GatewayAddress) = 0;
+ NetCopyMem (&Ip4Config->RouteTable[0].SubnetAddress, &Subnet, sizeof (EFI_IPv4_ADDRESS));
+ NetCopyMem (&Ip4Config->RouteTable[0].SubnetMask, &Dhcp4Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ NetZeroMem (&Ip4Config->RouteTable[0].GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
//
// Create a route if there is a default router.
//
- if (EFI_IP4 (Dhcp4Mode.RouterAddress) != 0) {
- Ip4Config->RouteTableSize = 2;
- EFI_IP4 (Ip4Config->RouteTable[1].SubnetAddress) = 0;
- EFI_IP4 (Ip4Config->RouteTable[1].SubnetMask) = 0;
- Ip4Config->RouteTable[1].GatewayAddress = Dhcp4Mode.RouterAddress;
+ if (!EFI_IP4_EQUAL (Dhcp4Mode.RouterAddress, mZeroIp4Addr)) {
+ Ip4Config->RouteTableSize = 2;
+
+ NetZeroMem (&Ip4Config->RouteTable[1].SubnetAddress, sizeof (EFI_IPv4_ADDRESS));
+ NetZeroMem (&Ip4Config->RouteTable[1].SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ NetCopyMem (&Ip4Config->RouteTable[1].GatewayAddress, &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
}
Instance->Result = EFI_SUCCESS;
diff --git a/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c b/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c
index 265135a117..3d857b047e 100644
--- a/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c
+++ b/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c
@@ -60,7 +60,6 @@ EfiIp4ConfigUnload (
return NetLibDefaultUnload (ImageHandle);
}
-//@MT: EFI_DRIVER_ENTRY_POINT (Ip4ConfigDriverEntryPoint)
EFI_STATUS
Ip4ConfigDriverEntryPoint (
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c
new file mode 100644
index 0000000000..728887587e
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c
@@ -0,0 +1,166 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+#include "Ip4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+Ip4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+Ip4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gIp4ComponentName = {
+ Ip4ComponentNameGetDriverName,
+ Ip4ComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mIp4DriverNameTable[] = {
+ {
+ "eng",
+ L"IP4 Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+Ip4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable
+ name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the driver name
+ that that the caller is requesting, and it must match
+ one of the languages specified in SupportedLanguages.
+ The number of languages supported by a driver is up to
+ the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the driver specified by This in the
+ language specified by Language.
+
+ Returns:
+ EFI_SUCCES - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gIp4ComponentName.SupportedLanguages,
+ mIp4DriverNameTable,
+ DriverName
+ );
+
+}
+
+EFI_STATUS
+EFIAPI
+Ip4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of
+ the controller that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language
+ specified by Language from the point of view of the
+ driver specified by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not
+ a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c
new file mode 100644
index 0000000000..ac339bee52
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c
@@ -0,0 +1,421 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Common.c
+
+Abstract:
+
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Return the cast type (Unicast/Boradcast) specific to a
+ interface. All the addresses are host byte ordered.
+
+ @param IpAddr The IP address to classify in host byte order
+ @param IpIf The interface that IpAddr received from
+
+ @return The cast type of this IP address specific to the interface.
+ @retval IP4_LOCAL_HOST The IpAddr equals to the interface's address
+ @retval IP4_SUBNET_BROADCAST The IpAddr is a directed subnet boradcast to the
+ interface
+ @retval IP4_NET_BROADCAST The IpAddr is a network broadcast to the interface
+
+**/
+INTN
+Ip4GetNetCast (
+ IN IP4_ADDR IpAddr,
+ IN IP4_INTERFACE *IpIf
+ )
+{
+ if (IpAddr == IpIf->Ip) {
+ return IP4_LOCAL_HOST;
+
+ } else if (IpAddr == IpIf->SubnetBrdcast) {
+ return IP4_SUBNET_BROADCAST;
+
+ } else if (IpAddr == IpIf->NetBrdcast) {
+ return IP4_NET_BROADCAST;
+
+ }
+
+ return 0;
+}
+
+
+/**
+ Find the cast type of the packet related to the local host.
+ This isn't the same as link layer cast type. For example, DHCP
+ server may send local broadcast to the local unicast MAC.
+
+ @param IpSb The IP4 service binding instance that received the
+ packet
+ @param Dst The destination address in the packet (host byte
+ order)
+ @param Src The source address in the packet (host byte order)
+
+ @return The cast type for the Dst, it will return on the first non-promiscuous
+ @return cast type to a configured interface. If the packet doesn't match any of
+ @return the interface, multicast address and local broadcast address are checked.
+
+**/
+INTN
+Ip4GetHostCast (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+ INTN Type;
+ INTN Class;
+
+ Type = 0;
+
+ if (IpSb->MnpConfigData.EnablePromiscuousReceive) {
+ Type = IP4_PROMISCUOUS;
+ }
+
+ //
+ // Go through the interface list of the IP service, most likely.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ //
+ // Skip the unconfigured interface and invalid source address:
+ // source address can't be broadcast.
+ //
+ if (!IpIf->Configured || IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {
+ continue;
+ }
+
+ if ((Class = Ip4GetNetCast (Dst, IpIf)) > Type) {
+ return Class;
+ }
+ }
+
+ //
+ // If it is local broadcast address. The source address must
+ // be a unicast address on one of the direct connected network.
+ // If it is a multicast address, accept it only if we are in
+ // the group.
+ //
+ if (Dst == IP4_ALLONE_ADDRESS) {
+ IpIf = Ip4FindNet (IpSb, Src);
+
+ if (IpIf && !IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {
+ return IP4_LOCAL_BROADCAST;
+ }
+
+ } else if (IP4_IS_MULTICAST (Dst) && Ip4FindGroup (&IpSb->IgmpCtrl, Dst)) {
+ return IP4_MULTICAST;
+ }
+
+ return Type;
+}
+
+
+/**
+ Find an interface whose configured IP address is Ip
+
+ @param IpSb The IP4 service binding instance
+ @param Ip The Ip address (host byte order) to find
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindInterface (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured && (IpIf->Ip == Ip)) {
+ return IpIf;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Find an interface that Ip is on that connected network.
+
+ @param IpSb The IP4 service binding instance
+ @param Ip The Ip address (host byte order) to find
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindNet (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured && IP4_NET_EQUAL (Ip, IpIf->Ip, IpIf->SubnetMask)) {
+ return IpIf;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Find an interface of the service with the same Ip/Netmask pair.
+
+ @param IpSb Ip4 service binding instance
+ @param Ip The Ip adress to find (host byte order)
+ @param Netmask The network to find (host byte order)
+
+ @return The IP4_INTERFACE point if found, otherwise NULL
+
+**/
+IP4_INTERFACE *
+Ip4FindStationAddress (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR Netmask
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured && (IpIf->Ip == Ip) && (IpIf->SubnetMask == Netmask)) {
+ return IpIf;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Get the MAC address for a multicast IP address. Call
+ Mnp's McastIpToMac to find the MAC address in stead of
+ hard code the NIC to be Ethernet.
+
+ @param Mnp The Mnp instance to get the MAC address.
+ @param Multicast The multicast IP address to translate.
+ @param Mac The buffer to hold the translated address.
+
+ @return Returns EFI_SUCCESS if the multicast IP is successfully
+ @return translated to a multicast MAC address. Otherwise some error.
+
+**/
+EFI_STATUS
+Ip4GetMulticastMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN IP4_ADDR Multicast,
+ OUT EFI_MAC_ADDRESS *Mac
+ )
+{
+ EFI_IP_ADDRESS EfiIp;
+
+ EFI_IP4 (EfiIp.v4) = HTONL (Multicast);
+ return Mnp->McastIpToMac (Mnp, FALSE, &EfiIp, Mac);
+}
+
+
+/**
+ Convert the multibyte field in IP header's byter order.
+ In spite of its name, it can also be used to convert from
+ host to network byte order.
+
+ @param Head The IP head to convert
+
+ @return Point to the converted IP head
+
+**/
+IP4_HEAD *
+Ip4NtohHead (
+ IN IP4_HEAD *Head
+ )
+{
+ Head->TotalLen = NTOHS (Head->TotalLen);
+ Head->Id = NTOHS (Head->Id);
+ Head->Fragment = NTOHS (Head->Fragment);
+ Head->Src = NTOHL (Head->Src);
+ Head->Dst = NTOHL (Head->Dst);
+
+ return Head;
+}
+
+
+/**
+ Set the Ip4 variable data.
+
+ @param IpSb Ip4 service binding instance
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+Ip4SetVariableData (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ UINT32 NumConfiguredInstance;
+ NET_LIST_ENTRY *Entry;
+ UINTN VariableDataSize;
+ EFI_IP4_VARIABLE_DATA *Ip4VariableData;
+ EFI_IP4_ADDRESS_PAIR *Ip4AddressPair;
+ IP4_PROTOCOL *IpInstance;
+ CHAR16 *NewMacString;
+ EFI_STATUS Status;
+
+ NumConfiguredInstance = 0;
+
+ //
+ // Go through the children list to count the configured children.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, Link, IP4_PROTOCOL_SIGNATURE);
+
+ if (IpInstance->State == IP4_STATE_CONFIGED) {
+ NumConfiguredInstance++;
+ }
+ }
+
+ //
+ // Calculate the size of the Ip4VariableData. As there may be no IP child,
+ // we should add extra buffer for the address paris only if the number of configured
+ // children is more than 1.
+ //
+ VariableDataSize = sizeof (EFI_IP4_VARIABLE_DATA);
+
+ if (NumConfiguredInstance > 1) {
+ VariableDataSize += sizeof (EFI_IP4_ADDRESS_PAIR) * (NumConfiguredInstance - 1);
+ }
+
+ Ip4VariableData = NetAllocatePool (VariableDataSize);
+ if (Ip4VariableData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ip4VariableData->DriverHandle = IpSb->Image;
+ Ip4VariableData->AddressCount = NumConfiguredInstance;
+
+ Ip4AddressPair = &Ip4VariableData->AddressPairs[0];
+
+ //
+ // Go through the children list to fill the configured children's address pairs.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, Link, IP4_PROTOCOL_SIGNATURE);
+
+ if (IpInstance->State == IP4_STATE_CONFIGED) {
+ Ip4AddressPair->InstanceHandle = IpInstance->Handle;
+ EFI_IP4 (Ip4AddressPair->Ip4Address) = NTOHL (IpInstance->Interface->Ip);
+ EFI_IP4 (Ip4AddressPair->SubnetMask) = NTOHL (IpInstance->Interface->SubnetMask);
+
+ Ip4AddressPair++;
+ }
+ }
+
+ //
+ // Get the mac string.
+ //
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (IpSb->MacString != NULL) {
+ //
+ // The variable is set already, we're going to update it.
+ //
+ if (StrCmp (IpSb->MacString, NewMacString) != 0) {
+ //
+ // The mac address is changed, delete the previous variable first.
+ //
+ gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+ }
+
+ NetFreePool (IpSb->MacString);
+ }
+
+ IpSb->MacString = NewMacString;
+
+ Status = gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ VariableDataSize,
+ (VOID *) Ip4VariableData
+ );
+
+ON_ERROR:
+
+ NetFreePool (Ip4VariableData);
+
+ return Status;
+}
+
+
+/**
+ Clear the variable and free the resource.
+
+ @param IpSb Ip4 service binding instance
+
+ @return None.
+
+**/
+VOID
+Ip4ClearVariableData (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ ASSERT (IpSb->MacString != NULL);
+
+ gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+
+ NetFreePool (IpSb->MacString);
+ IpSb->MacString = NULL;
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h
new file mode 100644
index 0000000000..59e5c11786
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h
@@ -0,0 +1,143 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Common.h
+
+Abstract:
+
+ Common definition for IP4.
+
+
+**/
+
+#ifndef __EFI_IP4_COMMON_H__
+#define __EFI_IP4_COMMON_H__
+
+typedef struct _IP4_INTERFACE IP4_INTERFACE;
+typedef struct _IP4_PROTOCOL IP4_PROTOCOL;
+typedef struct _IP4_SERVICE IP4_SERVICE;
+
+
+enum {
+ IP4_ETHER_PROTO = 0x0800,
+
+ IP4_PROTO_ICMP = 0x01,
+ IP4_PROTO_IGMP = 0x02,
+
+ //
+ // The packet is received as link level broadcast/multicast/promiscuous.
+ //
+ IP4_LINK_BROADCAST = 0x00000001,
+ IP4_LINK_MULTICAST = 0x00000002,
+ IP4_LINK_PROMISC = 0x00000004,
+
+ //
+ // IP4 address cast type classfication. Keep it true that any
+ // type bigger than or equal to LOCAL_BROADCAST is broadcast.
+ //
+ IP4_PROMISCUOUS = 1,
+ IP4_LOCAL_HOST,
+ IP4_MULTICAST,
+ IP4_LOCAL_BROADCAST, // Destination is 255.255.255.255
+ IP4_SUBNET_BROADCAST,
+ IP4_NET_BROADCAST,
+
+ //
+ // IP4 header flags
+ //
+ IP4_HEAD_DF_MASK = 0x4000,
+ IP4_HEAD_MF_MASK = 0x2000,
+ IP4_HEAD_OFFSET_MASK = 0x1fff,
+};
+
+#define IP4_ALLZERO_ADDRESS 0x00000000u
+#define IP4_ALLONE_ADDRESS 0xFFFFFFFFu
+#define IP4_ALLSYSTEM_ADDRESS 0xE0000001u
+#define IP4_ALLROUTER_ADDRESS 0xE0000002u
+
+//
+// Compose the fragment field to be used in the IP4 header.
+//
+#define IP4_HEAD_FRAGMENT_FIELD(Df, Mf, Offset) \
+ ((UINT16)(((Df) ? 0x4000 : 0) | ((Mf) ? 0x2000 : 0) | (((Offset) >> 3) & 0x1fff)))
+
+#define IP4_LAST_FRAGMENT(FragmentField) \
+ (((FragmentField) & IP4_HEAD_MF_MASK) == 0)
+
+#define IP4_FIRST_FRAGMENT(FragmentField) \
+ ((BOOLEAN)(((FragmentField) & IP4_HEAD_OFFSET_MASK) == 0))
+
+#define IP4_IS_BROADCAST(CastType) ((CastType) >= IP4_LOCAL_BROADCAST)
+
+//
+// Conver the Microsecond to second. IP transmit/receive time is
+// in the unit of microsecond. IP ticks once per second.
+//
+#define IP4_US_TO_SEC(Us) (((Us) + 999999) / 1000000)
+
+INTN
+Ip4GetNetCast (
+ IN IP4_ADDR IpAddr,
+ IN IP4_INTERFACE *IpIf
+ );
+
+INTN
+Ip4GetHostCast (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src
+ );
+
+IP4_INTERFACE *
+Ip4FindInterface (
+ IN IP4_SERVICE *IpService,
+ IN IP4_ADDR Addr
+ );
+
+IP4_INTERFACE *
+Ip4FindNet (
+ IN IP4_SERVICE *IpService,
+ IN IP4_ADDR Addr
+ );
+
+IP4_INTERFACE *
+Ip4FindStationAddress (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR Netmask
+ );
+
+EFI_STATUS
+Ip4GetMulticastMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN IP4_ADDR Multicast,
+ OUT EFI_MAC_ADDRESS *Mac
+ );
+
+IP4_HEAD *
+Ip4NtohHead (
+ IN IP4_HEAD *Head
+ );
+
+EFI_STATUS
+Ip4SetVariableData (
+ IN IP4_SERVICE *IpSb
+ );
+
+VOID
+Ip4ClearVariableData (
+ IN IP4_SERVICE *IpSb
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c
new file mode 100644
index 0000000000..eec6d69bd3
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c
@@ -0,0 +1,932 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Ip4Driver.c
+
+Abstract:
+
+ The driver binding and service binding protocol for IP4 driver.
+
+
+**/
+
+#include "Ip4Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding = {
+ Ip4DriverBindingSupported,
+ Ip4DriverBindingStart,
+ Ip4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+//@MT: EFI_DRIVER_ENTRY_POINT (Ip4DriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+Ip4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ The entry point for IP4 driver which install the driver
+ binding and component name protocol on its image.
+
+Arguments:
+
+ ImageHandle - The image handle of the driver
+ SystemTable - The system table
+
+Returns:
+
+ EFI_SUCCESS if the driver binding and component name protocols
+ are successfully installed, otherwise if failed.
+
+--*/
+{
+ return NetLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gIp4DriverBinding,
+ ImageHandle,
+ &gIp4ComponentName,
+ NULL,
+ NULL
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver supports this device
+ @retval EFI_ALREADY_STARTED This driver is already running on this device
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test for the MNP service binding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Test for the Arp service binding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+Ip4CleanService (
+ IN IP4_SERVICE *IpSb
+ );
+
+
+/**
+ Create a new IP4 driver service binding protocol
+
+ @param Controller The controller that has MNP service binding
+ installed
+ @param ImageHandle The IP4 driver's image handle
+ @param Service The variable to receive the newly created IP4
+ service.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource
+ @retval EFI_SUCCESS A new IP4 service binding private is created.
+
+**/
+STATIC
+EFI_STATUS
+Ip4CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ OUT IP4_SERVICE **Service
+ )
+{
+ IP4_SERVICE *IpSb;
+ EFI_STATUS Status;
+
+ ASSERT (Service != NULL);
+
+ *Service = NULL;
+
+ //
+ // allocate a service private data then initialize all the filed to
+ // empty resources, so if any thing goes wrong when allocating
+ // resources, Ip4CleanService can be called to clean it up.
+ //
+ IpSb = NetAllocatePool (sizeof (IP4_SERVICE));
+
+ if (IpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ IpSb->Signature = IP4_SERVICE_SIGNATURE;
+ IpSb->ServiceBinding.CreateChild = Ip4ServiceBindingCreateChild;
+ IpSb->ServiceBinding.DestroyChild = Ip4ServiceBindingDestroyChild;
+ IpSb->State = IP4_SERVICE_UNSTARTED;
+ IpSb->InDestory = FALSE;
+
+ IpSb->NumChildren = 0;
+ NetListInit (&IpSb->Children);
+
+ NetListInit (&IpSb->Interfaces);
+ IpSb->DefaultInterface = NULL;
+ IpSb->DefaultRouteTable = NULL;
+
+ Ip4InitAssembleTable (&IpSb->Assemble);
+
+ IpSb->IgmpCtrl.Igmpv1QuerySeen = 0;
+ NetListInit (&IpSb->IgmpCtrl.Groups);
+
+ IpSb->Image = ImageHandle;
+ IpSb->Controller = Controller;
+
+ IpSb->MnpChildHandle = NULL;
+ IpSb->Mnp = NULL;
+
+ IpSb->MnpConfigData.ReceivedQueueTimeoutValue = 0;
+ IpSb->MnpConfigData.TransmitQueueTimeoutValue = 0;
+ IpSb->MnpConfigData.ProtocolTypeFilter = IP4_ETHER_PROTO;
+ IpSb->MnpConfigData.EnableUnicastReceive = TRUE;
+ IpSb->MnpConfigData.EnableMulticastReceive = TRUE;
+ IpSb->MnpConfigData.EnableBroadcastReceive = TRUE;
+ IpSb->MnpConfigData.EnablePromiscuousReceive = FALSE;
+ IpSb->MnpConfigData.FlushQueuesOnReset = TRUE;
+ IpSb->MnpConfigData.EnableReceiveTimestamps = FALSE;
+ IpSb->MnpConfigData.DisableBackgroundPolling = FALSE;
+
+ NetZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));
+
+ IpSb->Timer = NULL;
+ IpSb->Ip4Config = NULL;
+ IpSb->DoneEvent = NULL;
+ IpSb->ReconfigEvent = NULL;
+
+ //
+ // Create various resources. First create the route table, timer
+ // event and MNP child. IGMP, interface's initialization depend
+ // on the MNP child.
+ //
+ IpSb->DefaultRouteTable = Ip4CreateRouteTable ();
+
+ if (IpSb->DefaultRouteTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Ip4TimerTicking,
+ IpSb,
+ &IpSb->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = NetLibCreateServiceChild (
+ Controller,
+ ImageHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ &IpSb->MnpChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &IpSb->Mnp,
+ ImageHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4ServiceConfigMnp (IpSb, TRUE);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4InitIgmp (IpSb);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpSb->DefaultInterface = Ip4CreateInterface (IpSb->Mnp, Controller, ImageHandle);
+
+ if (IpSb->DefaultInterface == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ NetListInsertHead (&IpSb->Interfaces, &IpSb->DefaultInterface->Link);
+
+ IpSb->MacString = NULL;
+
+ *Service = IpSb;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip4CleanService (IpSb);
+ NetFreePool (IpSb);
+
+ return Status;
+}
+
+
+/**
+ Clean up a IP4 service binding instance. It will release all
+ the resource allocated by the instance. The instance may be
+ partly initialized, or partly destoried. If a resource is
+ destoried, it is marked as that in case the destory failed and
+ being called again later.
+
+ @param IpSb The IP4 serviceing binding instance to clean up
+
+ @retval EFI_SUCCESS The resource used by the instance are cleaned up
+ @retval Others Failed to clean up some of the resources.
+
+**/
+EFI_STATUS
+Ip4CleanService (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ EFI_STATUS Status;
+
+ if (IpSb->DefaultInterface != NULL) {
+ Status = Ip4FreeInterface (IpSb->DefaultInterface, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ IpSb->DefaultInterface = NULL;
+ }
+
+ if (IpSb->DefaultRouteTable != NULL) {
+ Ip4FreeRouteTable (IpSb->DefaultRouteTable);
+ IpSb->DefaultRouteTable = NULL;
+ }
+
+ Ip4CleanAssembleTable (&IpSb->Assemble);
+
+ if (IpSb->MnpChildHandle != NULL) {
+ if (IpSb->Mnp) {
+ gBS->CloseProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ IpSb->Image,
+ IpSb->Controller
+ );
+
+ IpSb->Mnp = NULL;
+ }
+
+ NetLibDestroyServiceChild (
+ IpSb->Controller,
+ IpSb->Image,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ IpSb->MnpChildHandle
+ );
+
+ IpSb->MnpChildHandle = NULL;
+ }
+
+ if (IpSb->Timer != NULL) {
+ gBS->SetTimer (IpSb->Timer, TimerCancel, 0);
+ gBS->CloseEvent (IpSb->Timer);
+
+ IpSb->Timer = NULL;
+ }
+
+ if (IpSb->Ip4Config != NULL) {
+ IpSb->Ip4Config->Stop (IpSb->Ip4Config);
+
+ gBS->CloseProtocol (
+ IpSb->Controller,
+ &gEfiIp4ConfigProtocolGuid,
+ IpSb->Image,
+ IpSb->Controller
+ );
+
+ gBS->CloseEvent (IpSb->DoneEvent);
+ gBS->CloseEvent (IpSb->ReconfigEvent);
+ IpSb->Ip4Config = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ IP4_SERVICE *IpSb;
+ EFI_STATUS Status;
+
+ //
+ // Test for the Ip4 service binding protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Ip4CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Install the Ip4ServiceBinding Protocol onto ControlerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ &IpSb->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_SERVICE;
+ }
+
+ //
+ // ready to go: start the receiving and timer
+ //
+ Status = Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);
+
+ if (EFI_ERROR (Status)) {
+ goto UNINSTALL_PROTOCOL;
+ }
+
+ Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto UNINSTALL_PROTOCOL;
+ }
+
+ //
+ // Initialize the IP4 ID
+ //
+ mIp4Id = (UINT16)NET_RANDOM (NetRandomInitSeed ());
+
+ Ip4SetVariableData (IpSb);
+
+ return Status;
+
+UNINSTALL_PROTOCOL:
+ gBS->UninstallProtocolInterface (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ &IpSb->ServiceBinding
+ );
+
+FREE_SERVICE:
+ Ip4CleanService (IpSb);
+ NetFreePool (IpSb);
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero stop the entire bus driver.
+ @param ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCES This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ IP4_SERVICE *IpSb;
+ IP4_PROTOCOL *IpInstance;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ INTN State;
+
+ //
+ // IP4 driver opens the MNP child, ARP children or the IP4_CONFIG protocol
+ // by driver. So the ControllerHandle may be the MNP child handle, ARP child
+ // handle, or the NIC (UNDI) handle because IP4_CONFIG protocol is installed
+ // in the NIC handle.
+ //
+ //
+ // First, check whether it is the IP4_CONFIG protocol being uninstalled.
+ // IP4_CONFIG protocol is installed on the NIC handle. It isn't necessary
+ // to clean up the default configuration if IP4_CONFIG is being stopped.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ConfigProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ //
+ // Retrieve the IP4 service binding protocol. If failed, it is
+ // likely that Ip4 ServiceBinding is uninstalled already. In this
+ // case, return immediately.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+
+ IpSb = IP4_SERVICE_FROM_PROTOCOL (ServiceBinding);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (IpSb->Ip4Config && (IpSb->State != IP4_SERVICE_DESTORY)) {
+
+ IpSb->Ip4Config->Stop (IpSb->Ip4Config);
+
+ Status = gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiIp4ConfigProtocolGuid,
+ IpSb->Image,
+ ControllerHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // If the auto configure hasn't complete, mark it as not started.
+ //
+ if (IpSb->State == IP4_SERVICE_STARTED) {
+ IpSb->State = IP4_SERVICE_UNSTARTED;
+ }
+
+ IpSb->Ip4Config = NULL;
+ gBS->CloseEvent (IpSb->DoneEvent);
+ gBS->CloseEvent (IpSb->ReconfigEvent);
+ }
+
+ NET_RESTORE_TPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Either MNP or ARP protocol is being uninstalled. The controller
+ // handle is either the MNP child or ARP child. But, the IP4's
+ // service binding is installed on the NIC handle. So, need to open
+ // the protocol info to find the NIC handle.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);
+
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);
+ }
+
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Retrieve the IP4 service binding protocol
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ IpSb = IP4_SERVICE_FROM_PROTOCOL (ServiceBinding);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (IpSb->InDestory) {
+ NET_RESTORE_TPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ IpSb->InDestory = TRUE;
+
+ State = IpSb->State;
+ IpSb->State = IP4_SERVICE_DESTORY;
+
+ //
+ // Destory all the children first. If not all children are destoried,
+ // the IP driver can operate correctly, so restore it state. Don't
+ // use NET_LIST_FOR_EACH_SAFE here, because it will cache the next
+ // pointer, which may point to the child that has already been destoried.
+ // For example, if there are two child in the list, the first is UDP
+ // listen child, the send is the MTFTP's child. When Udp child is
+ // destoried, it will destory the MTFTP's child. Then Next point to
+ // a invalid child.
+ //
+ while (!NetListIsEmpty (&IpSb->Children)) {
+ IpInstance = NET_LIST_HEAD (&IpSb->Children, IP4_PROTOCOL, Link);
+ Ip4ServiceBindingDestroyChild (ServiceBinding, IpInstance->Handle);
+ }
+
+ if (IpSb->NumChildren != 0) {
+ IpSb->State = State;
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ //
+ // Clear the variable data.
+ //
+ Ip4ClearVariableData (IpSb);
+
+ //
+ // OK, clean other resources then uninstall the service binding protocol.
+ //
+ Status = Ip4CleanService (IpSb);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ NET_RESTORE_TPL (OldTpl);
+ NetFreePool (IpSb);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ IpSb->InDestory = FALSE;
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Creates a child handle with a set of I/O services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If it
+ is not NULL, then the I/O services are added to
+ the existing child handle.
+
+ @retval EFI_SUCCES The child handle was created with the I/O services
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_PROTOCOL *IpInstance;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ VOID *Mnp;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpSb = IP4_SERVICE_FROM_PROTOCOL (This);
+ IpInstance = NetAllocatePool (sizeof (IP4_PROTOCOL));
+
+ if (IpInstance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ip4InitProtocol (IpSb, IpInstance);
+
+ //
+ // Install Ip4 onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ &IpInstance->Ip4Proto,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpInstance->Handle = *ChildHandle;
+
+ //
+ // Open the Managed Network protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &Mnp,
+ gIp4DriverBinding.DriverBindingHandle,
+ IpInstance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ &IpInstance->Ip4Proto,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ //
+ // Insert it into the service binding instance.
+ //
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ NetListInsertTail (&IpSb->Children, &IpInstance->Link);
+ IpSb->NumChildren++;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ON_ERROR:
+
+ if (EFI_ERROR (Status)) {
+
+ Ip4CleanProtocol (IpInstance);
+
+ NetFreePool (IpInstance);
+ }
+
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a set of I/O services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The I/O services were removed from the child
+ handle
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services
+ that are being removed
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its I/O services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Ip4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ IP4_SERVICE *IpSb;
+ IP4_PROTOCOL *IpInstance;
+ EFI_IP4_PROTOCOL *Ip4;
+ EFI_TPL OldTpl;
+ INTN State;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ IpSb = IP4_SERVICE_FROM_PROTOCOL (This);
+
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Ip4,
+ gIp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (Ip4);
+
+ if (IpInstance->Service != IpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // A child can be destoried more than once. For example,
+ // Ip4DriverBindingStop will destory all of its children.
+ // when UDP driver is being stopped, it will destory all
+ // the IP child it opens.
+ //
+ if (IpInstance->State == IP4_STATE_DESTORY) {
+ NET_RESTORE_TPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ State = IpInstance->State;
+ IpInstance->State = IP4_STATE_DESTORY;
+
+ //
+ // Close the Managed Network protocol.
+ //
+ gBS->CloseProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ gIp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the IP4 protocol first. Many thing happens during
+ // this:
+ // 1. The consumer of the IP4 protocol will be stopped if it
+ // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is
+ // stopped, IP driver's stop function will be called, and uninstall
+ // EFI_IP4_PROTOCOL will trigger the UDP's stop function. This
+ // makes it possible to create the network stack bottom up, and
+ // stop it top down.
+ // 2. the upper layer will recycle the received packet. The recycle
+ // event's TPL is higher than this function. The recycle events
+ // will be called back before preceeding. If any packets not recycled,
+ // that means there is a resource leak.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ &IpInstance->Ip4Proto
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4CleanProtocol (IpInstance);
+
+ Ip4SetVariableData (IpSb);
+
+ if (EFI_ERROR (Status)) {
+ gBS->InstallMultipleProtocolInterfaces (
+ &ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ Ip4,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ NetListRemoveEntry (&IpInstance->Link);
+ IpSb->NumChildren--;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ NetFreePool (IpInstance);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ IpInstance->State = State;
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h
new file mode 100644
index 0000000000..5421c90c3b
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h
@@ -0,0 +1,84 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Driver.h
+
+Abstract:
+
+
+**/
+
+#ifndef __EFI_IP4_DRIVER_H__
+#define __EFI_IP4_DRIVER_H__
+
+#include <Protocol/ServiceBinding.h>
+
+extern EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gIp4ComponentName;
+
+//
+// Function prototype for the driver's entry point
+//
+EFI_STATUS
+EFIAPI
+Ip4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+//
+// Function prototypes for the Drivr Binding Protocol
+//
+EFI_STATUS
+EFIAPI
+Ip4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Ip4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Ip4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+//
+// Function ptototypes for the ServiceBinding Prococol
+//
+EFI_STATUS
+EFIAPI
+Ip4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+EFI_STATUS
+EFIAPI
+Ip4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf
new file mode 100644
index 0000000000..f45cc3f609
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf
@@ -0,0 +1,81 @@
+#/** @file
+# Component name for module Ip4
+#
+# Copyright (c) 2007, Intel Corporation
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Ip4Dxe
+ FILE_GUID = 9FB1A1F3-3B71-4324-B39A-745CBB015FFF
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = Ip4DriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ Ip4Driver.c
+ Ip4Option.h
+ Ip4Route.h
+ Ip4If.c
+ Ip4Igmp.h
+ Ip4Output.c
+ Ip4Icmp.c
+ Ip4Igmp.c
+ Ip4Impl.c
+ Ip4Common.h
+ Ip4Impl.h
+ Ip4Driver.h
+ Ip4Common.c
+ Ip4If.h
+ Ip4Option.c
+ Ip4Output.h
+ ComponentName.c
+ Ip4Input.h
+ Ip4Route.c
+ Ip4Icmp.h
+ Ip4Input.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ DebugLib
+ NetLib
+
+
+[Protocols]
+ gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiManagedNetworkServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp4ConfigProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiArpServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiManagedNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiArpProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa
new file mode 100644
index 0000000000..c2059dd116
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa
@@ -0,0 +1,101 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>Ip4Dxe</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>9FB1A1F3-3B71-4324-B39A-745CBB015FFF</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Ip4</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>Ip4Dxe</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiRuntimeServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>Ip4Input.c</Filename>
+ <Filename>Ip4Icmp.h</Filename>
+ <Filename>Ip4Route.c</Filename>
+ <Filename>Ip4Input.h</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>Ip4Output.h</Filename>
+ <Filename>Ip4Option.c</Filename>
+ <Filename>Ip4If.h</Filename>
+ <Filename>Ip4Common.c</Filename>
+ <Filename>Ip4Driver.h</Filename>
+ <Filename>Ip4Impl.h</Filename>
+ <Filename>Ip4Common.h</Filename>
+ <Filename>Ip4Impl.c</Filename>
+ <Filename>Ip4Igmp.c</Filename>
+ <Filename>Ip4Icmp.c</Filename>
+ <Filename>Ip4Output.c</Filename>
+ <Filename>Ip4Igmp.h</Filename>
+ <Filename>Ip4If.c</Filename>
+ <Filename>Ip4Route.h</Filename>
+ <Filename>Ip4Option.h</Filename>
+ <Filename>Ip4Driver.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiArpProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiManagedNetworkProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiIp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiArpServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiIp4ConfigProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiManagedNetworkServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiIp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>Ip4DriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c
new file mode 100644
index 0000000000..7f29c90869
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c
@@ -0,0 +1,372 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Icmp.c
+
+Abstract:
+
+
+**/
+
+#include "Ip4Impl.h"
+
+IP4_ICMP_CLASS
+mIcmpClass[] = {
+ {ICMP_ECHO_REPLY, ICMP_QUERY_MESSAGE },
+ {1, ICMP_INVALID_MESSAGE},
+ {2, ICMP_INVALID_MESSAGE},
+ {ICMP_DEST_UNREACHABLE, ICMP_ERROR_MESSAGE },
+ {ICMP_SOURCE_QUENCH, ICMP_ERROR_MESSAGE },
+ {ICMP_REDIRECT, ICMP_ERROR_MESSAGE },
+ {6, ICMP_INVALID_MESSAGE},
+ {7, ICMP_INVALID_MESSAGE},
+ {ICMP_ECHO_REQUEST, ICMP_QUERY_MESSAGE },
+ {9, ICMP_INVALID_MESSAGE},
+ {10, ICMP_INVALID_MESSAGE},
+ {ICMP_TIME_EXCEEDED, ICMP_ERROR_MESSAGE },
+ {ICMP_PARAMETER_PROBLEM, ICMP_ERROR_MESSAGE },
+ {ICMP_TIMESTAMP , ICMP_QUERY_MESSAGE },
+ {14, ICMP_INVALID_MESSAGE},
+ {ICMP_INFO_REQUEST , ICMP_QUERY_MESSAGE },
+ {ICMP_INFO_REPLY , ICMP_QUERY_MESSAGE },
+};
+
+EFI_IP4_ICMP_TYPE
+mIp4SupportedIcmp [23] = {
+ {ICMP_ECHO_REPLY, ICMP_DEFAULT_CODE },
+
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_PROTO_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_PORT_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_FRAGMENT_FAILED },
+ {ICMP_DEST_UNREACHABLE, ICMP_SOURCEROUTE_FAILED},
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNKNOWN },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNKNOWN },
+ {ICMP_DEST_UNREACHABLE, ICMP_SOURCE_ISOLATED },
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_PROHIBITED },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_PROHIBITED },
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACHABLE_TOS },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNREACHABLE_TOS},
+
+ {ICMP_SOURCE_QUENCH, ICMP_DEFAULT_CODE },
+
+ {ICMP_REDIRECT, ICMP_NET_REDIRECT },
+ {ICMP_REDIRECT, ICMP_HOST_REDIRECT },
+ {ICMP_REDIRECT, ICMP_NET_TOS_REDIRECT },
+ {ICMP_REDIRECT, ICMP_HOST_TOS_REDIRECT },
+
+ {ICMP_ECHO_REQUEST, ICMP_DEFAULT_CODE },
+
+ {ICMP_TIME_EXCEEDED, ICMP_TIMEOUT_IN_TRANSIT},
+ {ICMP_TIME_EXCEEDED, ICMp_TIMEOUT_REASSEMBLE},
+
+ {ICMP_PARAMETER_PROBLEM, ICMP_DEFAULT_CODE },
+};
+
+
+
+/**
+ Process the ICMP redirect. Find the instance then update
+ its route cache.
+ All kinds of redirect is treated as host redirect as
+ specified by RFC1122 3.3.1.2:
+ "Since the subnet mask appropriate to the destination
+ address is generally not known, a Network Redirect
+ message SHOULD be treated identically to a Host Redirect
+ message;"
+
+ @param IpSb The IP4 service binding instance that received the
+ packet
+ @param Head The IP head of the received ICMPpacket.
+ @param Packet The content of the ICMP redirect packet with IP
+ head removed.
+ @param Icmp The buffer to store the ICMP error message if
+ something is wrong.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid
+ @retval EFI_SUCCESS Successfully updated the route caches
+
+**/
+STATIC
+EFI_STATUS
+Ip4ProcessIcmpRedirect (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN IP4_ICMP_ERROR_HEAD *Icmp
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IP4_PROTOCOL *Ip4Instance;
+ IP4_ROUTE_CACHE_ENTRY *CacheEntry;
+ IP4_INTERFACE *IpIf;
+ IP4_ADDR Gateway;
+
+ //
+ // Find the interface whose IP address is the source of the
+ // orgianl IP packet.
+ //
+ IpIf = Ip4FindInterface (IpSb, NTOHL (Icmp->IpHead.Src));
+ Gateway = NTOHL (Icmp->Fourth);
+
+ //
+ // discard the packet if the new gateway address it specifies
+ // is not on the same connected net through which the Redirect
+ // arrived. (RFC1122 3.2.2.2).
+ //
+ if ((IpIf == NULL) || !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Update each IP child's route cache on the interface.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
+
+ if (Ip4Instance->RouteTable == NULL) {
+ continue;
+ }
+
+ CacheEntry = Ip4FindRouteCache (
+ Ip4Instance->RouteTable,
+ NTOHL (Icmp->IpHead.Dst),
+ NTOHL (Icmp->IpHead.Src)
+ );
+
+ //
+ // Only update the route cache's gateway if the source of the
+ // Redirect is the current first-hop gateway
+ //
+ if ((CacheEntry != NULL) && (NTOHL (Head->Src) == CacheEntry->NextHop)) {
+ CacheEntry->NextHop = Gateway;
+ }
+ }
+
+ NetbufFree (Packet);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Process the ICMP error packet. If it is an ICMP redirect packet,
+ update call Ip4ProcessIcmpRedirect to update the IP instance's
+ route cache, otherwise, deliver the packet to upper layer.
+
+ @param IpSb The IP service that received the packet.
+ @param Head The IP head of the ICMP error packet
+ @param Packet The content of the ICMP error with IP head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is invalid
+ @retval Others Failed to process the packet.
+ @retval EFI_SUCCESS The ICMP error is processed successfully.
+
+**/
+STATIC
+EFI_STATUS
+Ip4ProcessIcmpError (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_ERROR_HEAD Icmp;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ //
+ // If it is an ICMP redirect error, update the route cache
+ // as RFC1122. Otherwise, demultiplex it to IP instances.
+ //
+ if (Icmp.Head.Type == ICMP_REDIRECT) {
+ return Ip4ProcessIcmpRedirect (IpSb, Head, Packet, &Icmp);
+ }
+
+ IP4_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;
+ return Ip4Demultiplex (IpSb, Head, Packet);
+}
+
+
+/**
+ Replay an ICMP echo request.
+
+ @param IpSb The IP service that receivd the packet
+ @param Head The IP head of the ICMP error packet
+ @param Packet The content of the ICMP error with IP head
+ removed.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+ @retval EFI_SUCCESS The ICMP Echo request is successfully answered.
+ @retval Others Failed to answer the ICMP echo request.
+
+**/
+EFI_STATUS
+Ip4IcmpReplyEcho (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_QUERY_HEAD *Icmp;
+ NET_BUF *Data;
+ EFI_STATUS Status;
+ IP4_HEAD ReplyHead;
+
+ //
+ // make a copy the packet, it is really a bad idea to
+ // send the MNP's buffer back to MNP.
+ //
+ Data = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);
+
+ if (Data == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Change the ICMP type to echo reply, exchange the source
+ // and destination, then send it. The source is updated to
+ // use specific destination. See RFC1122. SRR/RR option
+ // update is omitted.
+ //
+ Icmp = (IP4_ICMP_QUERY_HEAD *) NetbufGetByte (Data, 0, NULL);
+ Icmp->Head.Type = ICMP_ECHO_REPLY;
+ Icmp->Head.Checksum = 0;
+ Icmp->Head.Checksum = ~NetblockChecksum ((UINT8 *) Icmp, Data->TotalSize);
+
+ ReplyHead.Tos = 0;
+ ReplyHead.Fragment = 0;
+ ReplyHead.Ttl = 64;
+ ReplyHead.Protocol = IP4_PROTO_ICMP;
+ ReplyHead.Src = 0;
+
+ //
+ // Ip4Output will select a source for us
+ //
+ ReplyHead.Dst = Head->Src;
+
+ Status = Ip4Output (
+ IpSb,
+ NULL,
+ Data,
+ &ReplyHead,
+ NULL,
+ 0,
+ IP4_ALLZERO_ADDRESS,
+ Ip4SysPacketSent,
+ NULL
+ );
+
+ON_EXIT:
+ NetbufFree (Packet);
+ return Status;
+}
+
+
+/**
+ Process the ICMP query message. If it is an ICMP echo
+ request, answer it. Otherwise deliver it to upper layer.
+
+ @param IpSb The IP service that receivd the packet
+ @param Head The IP head of the ICMP query packet
+ @param Packet The content of the ICMP query with IP head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is invalid
+ @retval EFI_SUCCESS The ICMP query message is processed
+
+**/
+EFI_STATUS
+Ip4ProcessIcmpQuery (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_QUERY_HEAD Icmp;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ if (Icmp.Head.Type == ICMP_ECHO_REQUEST) {
+ return Ip4IcmpReplyEcho (IpSb, Head, Packet);
+ }
+
+ return Ip4Demultiplex (IpSb, Head, Packet);
+}
+
+
+/**
+ Handle the ICMP packet. First validate the message format,
+ then according to the message types, process it as query or
+ error packet.
+
+ @param IpSb The IP service that receivd the packet
+ @param Head The IP head of the ICMP query packet
+ @param Packet The content of the ICMP query with IP head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is malformated.
+ @retval EFI_SUCCESS The ICMP message is successfully processed.
+
+**/
+EFI_STATUS
+Ip4IcmpHandle (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_HEAD Icmp;
+ UINT16 Checksum;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ goto DROP;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ if (Icmp.Type > ICMP_TYPE_MAX) {
+ goto DROP;
+ }
+
+ Checksum = ~NetbufChecksum (Packet);
+ if ((Icmp.Checksum != 0) && (Checksum != 0)) {
+ goto DROP;
+ }
+
+ if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
+ return Ip4ProcessIcmpError (IpSb, Head, Packet);
+
+ } else if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_QUERY_MESSAGE) {
+ return Ip4ProcessIcmpQuery (IpSb, Head, Packet);
+
+ }
+
+DROP:
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h
new file mode 100644
index 0000000000..f4641e2ef6
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h
@@ -0,0 +1,99 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Icmp.h
+
+Abstract:
+
+ Header file for ICMP protocol.
+
+
+**/
+
+#ifndef __EFI_IP4_ICMP_H__
+#define __EFI_IP4_ICMP_H__
+
+enum {
+ //
+ // ICMP type definations
+ //
+ ICMP_ECHO_REPLY = 0,
+ ICMP_DEST_UNREACHABLE = 3,
+ ICMP_SOURCE_QUENCH = 4,
+ ICMP_REDIRECT = 5,
+ ICMP_ECHO_REQUEST = 8,
+ ICMP_TIME_EXCEEDED = 11,
+ ICMP_PARAMETER_PROBLEM = 12,
+ ICMP_TIMESTAMP = 13,
+ ICMP_INFO_REQUEST = 15,
+ ICMP_INFO_REPLY = 16,
+ ICMP_TYPE_MAX = ICMP_INFO_REPLY,
+
+ ICMP_DEFAULT_CODE = 0,
+
+ //
+ // ICMP code definations for ICMP_DEST_UNREACHABLE
+ //
+ ICMP_NET_UNREACHABLE = 0,
+ ICMP_HOST_UNREACHABLE = 1,
+ ICMP_PROTO_UNREACHABLE = 2, // Host may generate
+ ICMP_PORT_UNREACHABLE = 3, // Host may generate
+ ICMP_FRAGMENT_FAILED = 4,
+ ICMP_SOURCEROUTE_FAILED = 5, // Host may generate
+ ICMP_NET_UNKNOWN = 6,
+ ICMP_HOST_UNKNOWN = 7,
+ ICMP_SOURCE_ISOLATED = 8,
+ ICMP_NET_PROHIBITED = 9,
+ ICMP_HOST_PROHIBITED = 10,
+ ICMP_NET_UNREACHABLE_TOS = 11,
+ ICMP_HOST_UNREACHABLE_TOS = 12,
+
+ //
+ // ICMP code definations for ICMP_TIME_EXCEEDED
+ //
+ ICMP_TIMEOUT_IN_TRANSIT = 0,
+ ICMp_TIMEOUT_REASSEMBLE = 1, // Host may generate
+
+ //
+ // ICMP code definations for ICMP_TIME_EXCEEDED
+ //
+ ICMP_NET_REDIRECT = 0,
+ ICMP_HOST_REDIRECT = 1,
+ ICMP_NET_TOS_REDIRECT = 2,
+ ICMP_HOST_TOS_REDIRECT = 3,
+
+ //
+ // ICMP message classes, each class of ICMP message shares
+ // a common message format. INVALID_MESSAGE is only a flag.
+ //
+ ICMP_INVALID_MESSAGE = 0,
+ ICMP_ERROR_MESSAGE = 1,
+ ICMP_QUERY_MESSAGE = 2,
+};
+
+typedef struct {
+ UINT8 IcmpType;
+ UINT8 IcmpClass;
+} IP4_ICMP_CLASS;
+
+extern IP4_ICMP_CLASS mIcmpClass[];
+extern EFI_IP4_ICMP_TYPE mIp4SupportedIcmp[];
+
+EFI_STATUS
+Ip4IcmpHandle (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Header,
+ IN NET_BUF *Packet
+ );
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c
new file mode 100644
index 0000000000..952eb98ccc
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c
@@ -0,0 +1,1146 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4If.c
+
+Abstract:
+
+ Implement IP4 pesudo interface.
+
+
+**/
+
+#include "Ip4Impl.h"
+
+//
+// Mac address with all zero, used to determine whethter the ARP
+// resolve succeeded. Failed ARP requests zero the MAC address buffer.
+//
+STATIC EFI_MAC_ADDRESS mZeroMacAddress;
+
+STATIC
+VOID
+EFIAPI
+Ip4OnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+STATIC
+VOID
+EFIAPI
+Ip4OnArpResolved (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+STATIC
+VOID
+EFIAPI
+Ip4OnFrameReceived (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+STATIC
+VOID
+Ip4CancelFrameArp (
+ IN IP4_ARP_QUE *ArpQue,
+ IN EFI_STATUS IoStatus,
+ IN IP4_FRAME_TO_CANCEL FrameToCancel, OPTIONAL
+ IN VOID *Context
+ );
+
+
+/**
+ Wrap a transmit request into a newly allocated IP4_LINK_TX_TOKEN.
+
+ @param Interface The interface to send out from
+ @param IpInstance The IpInstance that transmit the packet. NULL if
+ the packet is sent by the IP4 driver itself.
+ @param Packet The packet to transmit
+ @param CallBack Call back function to execute if transmission
+ finished.
+ @param Context Opaque parameter to the call back.
+
+ @return The wrapped token if succeed or NULL
+
+**/
+STATIC
+IP4_LINK_TX_TOKEN *
+Ip4WrapLinkTxToken (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance, OPTIONAL
+ IN NET_BUF *Packet,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData;
+ IP4_LINK_TX_TOKEN *Token;
+ EFI_STATUS Status;
+ UINT32 Count;
+
+ Token = NetAllocatePool (sizeof (IP4_LINK_TX_TOKEN) + \
+ (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));
+
+ if (Token == NULL) {
+ return NULL;
+ }
+
+ Token->Signature = IP4_FRAME_TX_SIGNATURE;
+ NetListInit (&Token->Link);
+
+ Token->Interface = Interface;
+ Token->IpInstance = IpInstance;
+ Token->CallBack = CallBack;
+ Token->Packet = Packet;
+ Token->Context = Context;
+ CopyMem (&Token->DstMac, &mZeroMacAddress, sizeof (EFI_MAC_ADDRESS));
+ CopyMem (&Token->SrcMac, &Interface->Mac, sizeof (EFI_MAC_ADDRESS));
+
+ MnpToken = &(Token->MnpToken);
+ MnpToken->Status = EFI_NOT_READY;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ip4OnFrameSent,
+ Token,
+ &MnpToken->Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetFreePool (Token);
+ return NULL;
+ }
+
+ MnpTxData = &Token->MnpTxData;
+ MnpToken->Packet.TxData = MnpTxData;
+
+ MnpTxData->DestinationAddress = &Token->DstMac;
+ MnpTxData->SourceAddress = &Token->SrcMac;
+ MnpTxData->ProtocolType = IP4_ETHER_PROTO;
+ MnpTxData->DataLength = Packet->TotalSize;
+ MnpTxData->HeaderLength = 0;
+
+ Count = Packet->BlockOpNum;
+
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);
+ MnpTxData->FragmentCount = (UINT16)Count;
+
+ return Token;
+}
+
+
+/**
+ Free the link layer transmit token. It will close the event
+ then free the memory used.
+
+ @param Token Token to free
+
+ @return NONE
+
+**/
+STATIC
+VOID
+Ip4FreeLinkTxToken (
+ IN IP4_LINK_TX_TOKEN *Token
+ )
+{
+ NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);
+
+ gBS->CloseEvent (Token->MnpToken.Event);
+ NetFreePool (Token);
+}
+
+
+/**
+ Create an IP_ARP_QUE structure to request ARP service.
+
+ @param Interface The interface to send ARP from.
+ @param DestIp The destination IP (host byte order) to request MAC
+ for
+
+ @return Point to newly created IP4_ARP_QUE if succeed, otherwise NULL.
+
+**/
+STATIC
+IP4_ARP_QUE *
+Ip4CreateArpQue (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_ADDR DestIp
+ )
+{
+ IP4_ARP_QUE *ArpQue;
+ EFI_STATUS Status;
+
+ ArpQue = NetAllocatePool (sizeof (IP4_ARP_QUE));
+
+ if (ArpQue == NULL) {
+ return NULL;
+ }
+
+ ArpQue->Signature = IP4_FRAME_ARP_SIGNATURE;
+ NetListInit (&ArpQue->Link);
+
+ NetListInit (&ArpQue->Frames);
+ ArpQue->Interface = Interface;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ip4OnArpResolved,
+ ArpQue,
+ &ArpQue->OnResolved
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetFreePool (ArpQue);
+ return NULL;
+ }
+
+ ArpQue->Ip = DestIp;
+ CopyMem (&ArpQue->Mac, &mZeroMacAddress, sizeof (EFI_MAC_ADDRESS));
+
+ return ArpQue;
+}
+
+
+/**
+ Remove all the transmit requests queued on the ARP queue, then free it.
+
+ @param ArpQue Arp queue to free
+ @param IoStatus The transmit status returned to transmit requests'
+ callback.
+
+ @return NONE
+
+**/
+STATIC
+VOID
+Ip4FreeArpQue (
+ IN IP4_ARP_QUE *ArpQue,
+ IN EFI_STATUS IoStatus
+ )
+{
+ NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);
+
+ //
+ // Remove all the frame waiting the ARP response
+ //
+ Ip4CancelFrameArp (ArpQue, IoStatus, NULL, NULL);
+
+ gBS->CloseEvent (ArpQue->OnResolved);
+ NetFreePool (ArpQue);
+}
+
+
+/**
+ Create a link layer receive token to wrap the receive request
+
+ @param Interface The interface to receive from
+ @param IpInstance The instance that request the receive (NULL for IP4
+ driver itself)
+ @param CallBack Call back function to execute when finished.
+ @param Context Opaque parameters to the callback
+
+ @return Point to created IP4_LINK_RX_TOKEN if succeed, otherwise NULL.
+
+**/
+STATIC
+IP4_LINK_RX_TOKEN *
+Ip4CreateLinkRxToken (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ IP4_LINK_RX_TOKEN *Token;
+ EFI_STATUS Status;
+
+ Token = NetAllocatePool (sizeof (IP4_LINK_RX_TOKEN));
+ if (Token == NULL) {
+ return NULL;
+ }
+
+ Token->Signature = IP4_FRAME_RX_SIGNATURE;
+ Token->Interface = Interface;
+ Token->IpInstance = IpInstance;
+ Token->CallBack = CallBack;
+ Token->Context = Context;
+
+ MnpToken = &Token->MnpToken;
+ MnpToken->Status = EFI_NOT_READY;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ip4OnFrameReceived,
+ Token,
+ &MnpToken->Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetFreePool (Token);
+ return NULL;
+ }
+
+ MnpToken->Packet.RxData = NULL;
+ return Token;
+}
+
+
+/**
+ Free the link layer request token. It will close the event
+ then free the memory used.
+
+ @param Token Request token to free
+
+ @return NONE
+
+**/
+STATIC
+VOID
+Ip4FreeFrameRxToken (
+ IN IP4_LINK_RX_TOKEN *Token
+ )
+{
+
+ NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);
+
+ gBS->CloseEvent (Token->MnpToken.Event);
+ NetFreePool (Token);
+}
+
+
+/**
+ Remove all the frames on the ARP queue that pass the FrameToCancel,
+ that is, either FrameToCancel is NULL or it returns true for the frame.
+
+ @param ArpQue ARP frame to remove the frames from.
+ @param IoStatus The status returned to the cancelled frames'
+ callback function.
+ @param FrameToCancel Function to select which frame to cancel.
+ @param Context Opaque parameter to the FrameToCancel.
+
+ @return NONE
+
+**/
+STATIC
+VOID
+Ip4CancelFrameArp (
+ IN IP4_ARP_QUE *ArpQue,
+ IN EFI_STATUS IoStatus,
+ IN IP4_FRAME_TO_CANCEL FrameToCancel, OPTIONAL
+ IN VOID *Context
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_LINK_TX_TOKEN *Token;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
+ Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
+
+ if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
+ NetListRemoveEntry (Entry);
+
+ Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context);
+ Ip4FreeLinkTxToken (Token);
+ }
+ }
+}
+
+
+/**
+ Remove all the frames on the interface that pass the FrameToCancel,
+ either queued on ARP queues or that have already been delivered to
+ MNP and not yet recycled.
+
+ @param Interface Interface to remove the frames from
+ @param IoStatus The transmit status returned to the frames'
+ callback
+ @param FrameToCancel Function to select the frame to cancel, NULL to
+ select all
+ @param Context Opaque parameters passed to FrameToCancel
+
+ @return NONE
+
+**/
+VOID
+Ip4CancelFrames (
+ IN IP4_INTERFACE *Interface,
+ IN EFI_STATUS IoStatus,
+ IN IP4_FRAME_TO_CANCEL FrameToCancel, OPTIONAL
+ IN VOID *Context
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_ARP_QUE *ArpQue;
+ IP4_LINK_TX_TOKEN *Token;
+
+ //
+ // Cancel all the pending frames on ARP requests
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {
+ ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);
+
+ Ip4CancelFrameArp (ArpQue, IoStatus, FrameToCancel, Context);
+
+ if (NetListIsEmpty (&ArpQue->Frames)) {
+ NetListRemoveEntry (Entry);
+
+ Interface->Arp->Cancel (Interface->Arp, &ArpQue->Ip, ArpQue->OnResolved);
+ Ip4FreeArpQue (ArpQue, EFI_ABORTED);
+ }
+ }
+
+ //
+ // Cancel all the frames that have been delivered to MNP
+ // but not yet recycled.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {
+ Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
+
+ if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
+ NetListRemoveEntry (Entry);
+
+ Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);
+ Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context);
+ Ip4FreeLinkTxToken (Token);
+ }
+ }
+}
+
+
+/**
+ Create an IP4_INTERFACE. Delay the creation of ARP instance until
+ the interface is configured.
+
+ @param Mnp The shared MNP child of this IP4 service binding
+ instance
+ @param Controller The controller this IP4 service binding instance
+ is installed. Most like the UNDI handle.
+ @param ImageHandle This driver's image handle
+
+ @return Point to the created IP4_INTERFACE, otherwise NULL.
+
+**/
+IP4_INTERFACE *
+Ip4CreateInterface (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ IP4_INTERFACE *Interface;
+ EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+ Interface = NetAllocatePool (sizeof (IP4_INTERFACE));
+
+ if ((Interface == NULL) || (Mnp == NULL)) {
+ return NULL;
+ }
+
+ Interface->Signature = IP4_INTERFACE_SIGNATURE;
+ NetListInit (&Interface->Link);
+ Interface->RefCnt = 1;
+
+ Interface->Ip = IP4_ALLZERO_ADDRESS;
+ Interface->SubnetMask = IP4_ALLZERO_ADDRESS;
+ Interface->Configured = FALSE;
+
+ Interface->Controller = Controller;
+ Interface->Image = ImageHandle;
+ Interface->Mnp = Mnp;
+ Interface->Arp = NULL;
+ Interface->ArpHandle = NULL;
+
+ NetListInit (&Interface->ArpQues);
+ NetListInit (&Interface->SentFrames);
+
+ Interface->RecvRequest = NULL;
+
+ //
+ // Get the interface's Mac address and broadcast mac address from SNP
+ //
+ if (EFI_ERROR (Mnp->GetModeData (Mnp, NULL, &SnpMode))) {
+ NetFreePool (Interface);
+ return NULL;
+ }
+
+ CopyMem (&Interface->Mac, &SnpMode.CurrentAddress, sizeof (EFI_MAC_ADDRESS));
+ CopyMem (&Interface->BroadcastMac, &SnpMode.BroadcastAddress, sizeof (EFI_MAC_ADDRESS));
+ Interface->HwaddrLen = SnpMode.HwAddressSize;
+
+ NetListInit (&Interface->IpInstances);
+ Interface->PromiscRecv = FALSE;
+
+ return Interface;
+}
+
+
+/**
+ Set the interface's address, create and configure
+ the ARP child if necessary.
+
+ @param Interface The interface to set the address
+ @param IpAddr The interface's IP address
+ @param SubnetMask The interface's netmask
+
+ @retval EFI_SUCCESS The interface is configured with Ip/netmask pair,
+ and a ARP is created for it.
+ @retval Others Failed to set the interface's address.
+
+**/
+EFI_STATUS
+Ip4SetAddress (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_ADDR IpAddr,
+ IN IP4_ADDR SubnetMask
+ )
+{
+ EFI_ARP_CONFIG_DATA ArpConfig;
+ EFI_STATUS Status;
+ INTN Type;
+ INTN Len;
+ IP4_ADDR Netmask;
+
+ NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
+
+ ASSERT (!Interface->Configured);
+
+ //
+ // Set the ip/netmask, then compute the subnet broadcast
+ // and network broadcast for easy access. When computing
+ // nework broadcast, the subnet mask is most like longer
+ // than the default netmask (not subneted) as defined in
+ // RFC793. If that isn't the case, we are aggregating the
+ // networks, use the subnet's mask instead.
+ //
+ Interface->Ip = IpAddr;
+ Interface->SubnetMask = SubnetMask;
+ Interface->SubnetBrdcast = (IpAddr | ~SubnetMask);
+
+ Type = NetGetIpClass (IpAddr);
+ Len = NetGetMaskLength (SubnetMask);
+ Netmask = mIp4AllMasks[NET_MIN (Len, Type << 3)];
+ Interface->NetBrdcast = (IpAddr | ~Netmask);
+
+ //
+ // If the address is NOT all zero, create then configure an ARP child.
+ // Pay attention: DHCP configures its station address as 0.0.0.0/0
+ //
+ Interface->Arp = NULL;
+ Interface->ArpHandle = NULL;
+
+ if (IpAddr != IP4_ALLZERO_ADDRESS) {
+ Status = NetLibCreateServiceChild (
+ Interface->Controller,
+ Interface->Image,
+ &gEfiArpServiceBindingProtocolGuid,
+ &Interface->ArpHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;;
+ }
+
+ Status = gBS->OpenProtocol (
+ Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ (VOID **) &Interface->Arp,
+ Interface->Image,
+ Interface->Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpAddr = HTONL (IpAddr);
+ ArpConfig.SwAddressType = IP4_ETHER_PROTO;
+ ArpConfig.SwAddressLength = 4;
+ ArpConfig.StationAddress = &IpAddr;
+ ArpConfig.EntryTimeOut = 0;
+ ArpConfig.RetryCount = 0;
+ ArpConfig.RetryTimeOut = 0;
+
+ Status = Interface->Arp->Configure (Interface->Arp, &ArpConfig);
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ Interface->Image,
+ Interface->Controller
+ );
+
+ goto ON_ERROR;
+ }
+ }
+
+ Interface->Configured = TRUE;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ NetLibDestroyServiceChild (
+ Interface->Controller,
+ Interface->Image,
+ &gEfiArpServiceBindingProtocolGuid,
+ &Interface->ArpHandle
+ );
+
+ return Status;
+}
+
+
+/**
+ Fileter function to cancel all the frame related to an IP instance.
+
+ @param Frame The transmit request to test whether to cancel
+ @param Context The context which is the Ip instance that issued
+ the transmit.
+
+ @retval TRUE The frame belongs to this instance and is to be
+ removed
+ @retval FALSE The frame doesn't belong to this instance.
+
+**/
+STATIC
+BOOLEAN
+Ip4CancelInstanceFrame (
+ IN IP4_LINK_TX_TOKEN *Frame,
+ IN VOID *Context
+ )
+{
+ if (Frame->IpInstance == (IP4_PROTOCOL *) Context) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+/**
+ If there is a pending receive request, cancel it. Don't call
+ the receive request's callback because this function can be only
+ called if the instance or driver is tearing itself down. It
+ doesn't make sense to call it back. But it is necessary to call
+ the transmit token's callback to give it a chance to free the
+ packet and update the upper layer's transmit request status, say
+ that from the UDP.
+
+ @param Interface The interface used by the IpInstance
+
+ @return None
+
+**/
+VOID
+Ip4CancelReceive (
+ IN IP4_INTERFACE *Interface
+ )
+{
+ EFI_TPL OldTpl;
+ IP4_LINK_RX_TOKEN *Token;
+
+ if ((Token = Interface->RecvRequest) != NULL) {
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ Interface->RecvRequest = NULL;
+ Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);
+ Ip4FreeFrameRxToken (Token);
+
+ NET_RESTORE_TPL (OldTpl);
+ }
+}
+
+
+/**
+ Free the interface used by IpInstance. All the IP instance with
+ the same Ip/Netmask pair share the same interface. It is reference
+ counted. All the frames haven't been sent will be cancelled.
+ Because the IpInstance is optional, the caller must remove
+ IpInstance from the interface's instance list itself.
+
+ @param Interface The interface used by the IpInstance
+ @param IpInstance The Ip instance that free the interface. NULL if
+ the Ip driver is releasing the default interface.
+
+ @retval EFI_SUCCESS The interface use IpInstance is freed.
+
+**/
+EFI_STATUS
+Ip4FreeInterface (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL
+ )
+{
+ NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
+ ASSERT (Interface->RefCnt > 0);
+
+ //
+ // Remove all the pending transmit token related to this IP instance.
+ //
+ Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, IpInstance);
+
+ if (--Interface->RefCnt > 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Destory the interface if this is the last IP instance that
+ // has the address. Remove all the system transmitted packets
+ // from this interface, cancel the receive request if there is
+ // one, and destory the ARP requests.
+ //
+ Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, NULL);
+ Ip4CancelReceive (Interface);
+
+ ASSERT (NetListIsEmpty (&Interface->IpInstances));
+ ASSERT (NetListIsEmpty (&Interface->ArpQues));
+ ASSERT (NetListIsEmpty (&Interface->SentFrames));
+
+ if (Interface->Arp != NULL) {
+ gBS->CloseProtocol (
+ Interface->ArpHandle,
+ &gEfiArpProtocolGuid,
+ Interface->Image,
+ Interface->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Interface->Controller,
+ Interface->Image,
+ &gEfiArpServiceBindingProtocolGuid,
+ Interface->ArpHandle
+ );
+ }
+
+ NetListRemoveEntry (&Interface->Link);
+ NetFreePool (Interface);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Callback function when ARP request are finished. It will cancelled
+ all the queued frame if the ARP requests failed. Or transmit them
+ if the request succeed.
+
+ @param Event The Arp request event
+ @param Context The context of the callback, a point to the ARP
+ queue
+
+ @return None
+
+**/
+STATIC
+VOID
+EFIAPI
+Ip4OnArpResolved (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_ARP_QUE *ArpQue;
+ IP4_INTERFACE *Interface;
+ IP4_LINK_TX_TOKEN *Token;
+ EFI_STATUS Status;
+
+ ArpQue = (IP4_ARP_QUE *) Context;
+ NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);
+
+ NetListRemoveEntry (&ArpQue->Link);
+
+ //
+ // ARP resolve failed for some reason. Release all the frame
+ // and ARP queue itself. Ip4FreeArpQue will call the frame's
+ // owner back.
+ //
+ if (NET_MAC_EQUAL (&ArpQue->Mac, &mZeroMacAddress, ArpQue->Interface->HwaddrLen)) {
+ Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);
+
+ return ;
+ }
+
+ //
+ // ARP resolve succeeded, Transmit all the frame. Release the ARP
+ // queue. It isn't necessary for us to cache the ARP binding because
+ // we always check the ARP cache first before transmit.
+ //
+ Interface = ArpQue->Interface;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
+ NetListRemoveEntry (Entry);
+
+ Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
+ CopyMem (&Token->DstMac, &ArpQue->Mac, sizeof (EFI_MAC_ADDRESS));
+
+ Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);
+
+ if (EFI_ERROR (Status)) {
+ Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context);
+
+ Ip4FreeLinkTxToken (Token);
+ continue;
+ }
+
+ NetListInsertTail (&Interface->SentFrames, &Token->Link);
+ }
+
+ Ip4FreeArpQue (ArpQue, EFI_SUCCESS);
+}
+
+
+/**
+ Callback funtion when frame transmission is finished. It will
+ call the frame owner's callback function to tell it the result.
+
+ @param Event The transmit token's event
+ @param Context Context which is point to the token.
+
+ @return None.
+
+**/
+STATIC
+VOID
+EFIAPI
+Ip4OnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP4_LINK_TX_TOKEN *Token;
+
+ Token = (IP4_LINK_TX_TOKEN *) Context;
+ NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);
+
+ NetListRemoveEntry (&Token->Link);
+
+ Token->CallBack (
+ Token->IpInstance,
+ Token->Packet,
+ Token->MnpToken.Status,
+ 0,
+ Token->Context
+ );
+
+ Ip4FreeLinkTxToken (Token);
+}
+
+
+
+/**
+ Send a frame from the interface. If the next hop is broadcast or
+ multicast address, it is transmitted immediately. If the next hop
+ is a unicast, it will consult ARP to resolve the NextHop's MAC.
+ If some error happened, the CallBack won't be called. So, the caller
+ must test the return value, and take action when there is an error.
+
+ @param Interface The interface to send the frame from
+ @param IpInstance The IP child that request the transmission. NULL
+ if it is the IP4 driver itself.
+ @param Packet The packet to transmit.
+ @param NextHop The immediate destination to transmit the packet
+ to.
+ @param CallBack Function to call back when transmit finished.
+ @param Context Opaque parameter to the call back.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame
+ @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop
+ @retval EFI_SUCCESS The packet is successfully transmitted.
+
+**/
+EFI_STATUS
+Ip4SendFrame (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance, OPTIONAL
+ IN NET_BUF *Packet,
+ IN IP4_ADDR NextHop,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ IP4_LINK_TX_TOKEN *Token;
+ NET_LIST_ENTRY *Entry;
+ IP4_ARP_QUE *ArpQue;
+ EFI_ARP_PROTOCOL *Arp;
+ EFI_STATUS Status;
+
+ ASSERT (Interface->Configured);
+
+ Token = Ip4WrapLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);
+
+ if (Token == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Get the destination MAC address for multicast and broadcasts.
+ // Don't depend on ARP to solve the address since there maybe no
+ // ARP at all. Ip4Output has set NextHop to 255.255.255.255 for
+ // all the broadcasts.
+ //
+ if (NextHop == IP4_ALLONE_ADDRESS) {
+ CopyMem (&Token->DstMac, &Interface->BroadcastMac, sizeof (EFI_MAC_ADDRESS));
+ goto SEND_NOW;
+
+ } else if (IP4_IS_MULTICAST (NextHop)) {
+
+ Status = Ip4GetMulticastMac (Interface->Mnp, NextHop, &Token->DstMac);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ goto SEND_NOW;
+ }
+
+ //
+ // Can only send out multicast/broadcast if the IP address is zero
+ //
+ if ((Arp = Interface->Arp) == NULL) {
+ Status = EFI_NO_MAPPING;
+ goto ON_ERROR;
+ }
+
+ //
+ // First check whether this binding is in the ARP cache.
+ //
+ NextHop = HTONL (NextHop);
+ Status = Arp->Request (Arp, &NextHop, NULL, &Token->DstMac);
+
+ if (Status == EFI_SUCCESS) {
+ goto SEND_NOW;
+
+ } else if (Status != EFI_NOT_READY) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Have to do asynchronous ARP resolution. First check
+ // whether there is already a pending request.
+ //
+ ArpQue = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {
+ ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);
+
+ if (ArpQue->Ip == NextHop) {
+ break;
+ }
+ }
+
+ //
+ // Found a pending ARP request, enqueue the frame then return
+ //
+ if (Entry != &Interface->ArpQues) {
+ NetListInsertTail (&ArpQue->Frames, &Token->Link);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // First frame to NextHop, issue an asynchronous ARP requests
+ //
+ ArpQue = Ip4CreateArpQue (Interface, NextHop);
+
+ if (ArpQue == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ Status = Arp->Request (Arp, &ArpQue->Ip, ArpQue->OnResolved, ArpQue->Mac.Addr);
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
+ Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);
+ goto ON_ERROR;
+ }
+
+ NetListInsertHead (&ArpQue->Frames, &Token->Link);
+ NetListInsertHead (&Interface->ArpQues, &ArpQue->Link);
+ return EFI_SUCCESS;
+
+SEND_NOW:
+ Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ NetListInsertTail (&Interface->SentFrames, &Token->Link);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip4FreeLinkTxToken (Token);
+ return Status;
+}
+
+
+/**
+ Call back function when the received packet is freed.
+ Check Ip4OnFrameReceived for information.
+
+ @param Context Context, which is the IP4_LINK_RX_TOKEN.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Ip4RecycleFrame (
+ IN VOID *Context
+ )
+{
+ IP4_LINK_RX_TOKEN *Frame;
+
+ Frame = (IP4_LINK_RX_TOKEN *) Context;
+ NET_CHECK_SIGNATURE (Frame, IP4_FRAME_RX_SIGNATURE);
+
+ gBS->SignalEvent (Frame->MnpToken.Packet.RxData->RecycleEvent);
+ Ip4FreeFrameRxToken (Frame);
+}
+
+
+/**
+ Received a frame from MNP, wrap it in net buffer then deliver
+ it to IP's input function. The ownship of the packet also
+ transferred to IP. When Ip is finished with this packet, it
+ will call NetbufFree to release the packet, NetbufFree will
+ again call the Ip4RecycleFrame to signal MNP's event and free
+ the token used.
+
+ @param Event The receive event delivered to MNP for receive.
+ @param Context Context for the callback.
+
+ @return None.
+
+**/
+STATIC
+VOID
+EFIAPI
+Ip4OnFrameReceived (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData;
+ IP4_LINK_RX_TOKEN *Token;
+ NET_FRAGMENT Netfrag;
+ NET_BUF *Packet;
+ UINT32 Flag;
+
+ Token = (IP4_LINK_RX_TOKEN *) Context;
+ NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);
+
+ //
+ // First clear the interface's receive request in case the
+ // caller wants to call Ip4ReceiveFrame in the callback.
+ //
+ Token->Interface->RecvRequest = NULL;
+
+ MnpToken = &Token->MnpToken;
+ MnpRxData = MnpToken->Packet.RxData;
+
+ if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {
+ Token->CallBack (Token->IpInstance, NULL, MnpToken->Status, 0, Token->Context);
+ Ip4FreeFrameRxToken (Token);
+
+ return ;
+ }
+
+ //
+ // Wrap the frame in a net buffer then deliever it to IP input.
+ // IP will reassemble the packet, and deliver it to upper layer
+ //
+ Netfrag.Len = MnpRxData->DataLength;
+ Netfrag.Bulk = MnpRxData->PacketData;
+
+ Packet = NetbufFromExt (&Netfrag, 1, 0, IP4_MAX_HEADLEN, Ip4RecycleFrame, Token);
+
+ if (Packet == NULL) {
+ gBS->SignalEvent (MnpRxData->RecycleEvent);
+
+ Token->CallBack (Token->IpInstance, NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);
+ Ip4FreeFrameRxToken (Token);
+
+ return ;
+ }
+
+ Flag = (MnpRxData->BroadcastFlag ? IP4_LINK_BROADCAST : 0);
+ Flag |= (MnpRxData->MulticastFlag ? IP4_LINK_MULTICAST : 0);
+ Flag |= (MnpRxData->PromiscuousFlag ? IP4_LINK_PROMISC : 0);
+
+ Token->CallBack (Token->IpInstance, Packet, EFI_SUCCESS, Flag, Token->Context);
+}
+
+
+/**
+ Request to receive the packet from the interface.
+
+ @param Interface The interface to receive the frames from
+ @param IpInstance The instance that requests the receive. NULL for
+ the driver itself.
+ @param CallBack Function to call when receive finished.
+ @param Context Opaque parameter to the callback
+
+ @retval EFI_ALREADY_STARTED There is already a pending receive request.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive
+ @retval EFI_SUCCESS The recieve request has been started.
+
+**/
+EFI_STATUS
+Ip4ReceiveFrame (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance, OPTIONAL
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ IP4_LINK_RX_TOKEN *Token;
+ EFI_STATUS Status;
+
+ NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
+
+ if (Interface->RecvRequest != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Token = Ip4CreateLinkRxToken (Interface, IpInstance, CallBack, Context);
+
+ if (Token == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Interface->Mnp->Receive (Interface->Mnp, &Token->MnpToken);
+
+ if (EFI_ERROR (Status)) {
+ Ip4FreeFrameRxToken (Token);
+ return Status;
+ }
+
+ Interface->RecvRequest = Token;
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h
new file mode 100644
index 0000000000..a324c82430
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h
@@ -0,0 +1,238 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4If.h
+
+Abstract:
+
+ Definition for IP4 pesudo interface structure.
+
+
+**/
+
+#ifndef __EFI_IP4_IF_H__
+#define __EFI_IP4_IF_H__
+
+enum {
+ IP4_FRAME_RX_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', 'F', 'R'),
+ IP4_FRAME_TX_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', 'F', 'T'),
+ IP4_FRAME_ARP_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', 'F', 'A'),
+ IP4_INTERFACE_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', 'I', 'F'),
+};
+
+//
+// This prototype is used by both receive and transmission.
+// When receiving Netbuf is allocated by IP4_INTERFACE, and
+// released by IP4. Flag shows whether the frame is received
+// as link broadcast/multicast...
+//
+// When transmitting, the Netbuf is from IP4, and provided
+// to the callback as a reference. Flag isn't used.
+//
+// IpInstance can be NULL which means that it is the IP4 driver
+// itself sending the packets. IP4 driver may send packets that
+// don't belong to any instance, such as ICMP errors, ICMP echo
+// responses, or IGMP packets. IpInstance is used as a tag in
+// this module.
+//
+typedef
+VOID
+(*IP4_FRAME_CALLBACK) (
+ IP4_PROTOCOL *IpInstance, OPTIONAL
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 LinkFlag,
+ VOID *Context
+ );
+
+//
+// Each receive request is wrapped in an IP4_LINK_RX_TOKEN.
+// Upon completion, the Callback will be called. Only one
+// receive request is send to MNP. IpInstance is always NULL.
+// Reference MNP's spec for information.
+//
+typedef struct {
+ UINT32 Signature;
+ IP4_INTERFACE *Interface;
+
+ IP4_PROTOCOL *IpInstance;
+ IP4_FRAME_CALLBACK CallBack;
+ VOID *Context;
+
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken;
+} IP4_LINK_RX_TOKEN;
+
+//
+// Each transmit request is wrapped in an IP4_LINK_TX_TOKEN.
+// Upon completion, the Callback will be called.
+//
+typedef struct {
+ UINT32 Signature;
+ NET_LIST_ENTRY Link;
+
+ IP4_INTERFACE *Interface;
+
+ IP4_PROTOCOL *IpInstance;
+ IP4_FRAME_CALLBACK CallBack;
+ NET_BUF *Packet;
+ VOID *Context;
+
+ EFI_MAC_ADDRESS DstMac;
+ EFI_MAC_ADDRESS SrcMac;
+
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA MnpTxData;
+} IP4_LINK_TX_TOKEN;
+
+//
+// Only one ARP request is requested for all the frames in
+// a time. It is started for the first frames to the Ip. Any
+// subsequent transmission frame will be linked to Frames, and
+// be sent all at once the ARP requests succeed.
+//
+typedef struct {
+ UINT32 Signature;
+ NET_LIST_ENTRY Link;
+
+ NET_LIST_ENTRY Frames;
+ IP4_INTERFACE *Interface;
+
+ //
+ // ARP requesting staffs
+ //
+ EFI_EVENT OnResolved;
+ IP4_ADDR Ip;
+ EFI_MAC_ADDRESS Mac;
+} IP4_ARP_QUE;
+
+//
+// Callback to select which frame to cancel. Caller can cancel a
+// single frame, or all the frame from an IP instance.
+//
+typedef
+BOOLEAN
+(*IP4_FRAME_TO_CANCEL) (
+ IP4_LINK_TX_TOKEN *Frame,
+ VOID *Context
+ );
+
+//
+// Each IP4 instance has its own station address. All the instances
+// with the same station address share a single interface structure.
+// Each interface has its own ARP child, and shares one MNP child.
+// Notice the special cases that DHCP can configure the interface
+// with 0.0.0.0/0.0.0.0.
+//
+typedef struct _IP4_INTERFACE {
+ UINT32 Signature;
+ NET_LIST_ENTRY Link;
+ INTN RefCnt;
+
+ //
+ // IP address and subnet mask of the interface. It also contains
+ // the subnet/net broadcast address for quick access. The fileds
+ // are invalid if (Configured == FALSE)
+ //
+ IP4_ADDR Ip;
+ IP4_ADDR SubnetMask;
+ IP4_ADDR SubnetBrdcast;
+ IP4_ADDR NetBrdcast;
+ BOOLEAN Configured;
+
+ //
+ // Handle used to create/destory ARP child. All the IP children
+ // share one MNP which is owned by IP service binding.
+ //
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ EFI_ARP_PROTOCOL *Arp;
+ EFI_HANDLE ArpHandle;
+
+ //
+ // Queues to keep the frames sent and waiting ARP request.
+ //
+ NET_LIST_ENTRY ArpQues;
+ NET_LIST_ENTRY SentFrames;
+ IP4_LINK_RX_TOKEN *RecvRequest;
+
+ //
+ // The interface's MAC and broadcast MAC address.
+ //
+ EFI_MAC_ADDRESS Mac;
+ EFI_MAC_ADDRESS BroadcastMac;
+ UINT32 HwaddrLen;
+
+ //
+ // All the IP instances that have the same IP/SubnetMask are linked
+ // together through IpInstances. If any of the instance enables
+ // promiscuous receive, PromiscRecv is true.
+ //
+ NET_LIST_ENTRY IpInstances;
+ BOOLEAN PromiscRecv;
+} IP4_INTERFACE;
+
+IP4_INTERFACE *
+Ip4CreateInterface (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle
+ );
+
+EFI_STATUS
+Ip4SetAddress (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_ADDR IpAddr,
+ IN IP4_ADDR SubnetMask
+ );
+
+EFI_STATUS
+Ip4FreeInterface (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance OPTIONAL
+ );
+
+EFI_STATUS
+Ip4SendFrame (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance, OPTIONAL
+ IN NET_BUF *Packet,
+ IN IP4_ADDR NextHop,
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ );
+
+VOID
+Ip4CancelFrames (
+ IN IP4_INTERFACE *Interface,
+ IN EFI_STATUS IoStatus,
+ IN IP4_FRAME_TO_CANCEL FrameToCancel, OPTIONAL
+ IN VOID *Context
+ );
+
+VOID
+Ip4CancelReceive (
+ IN IP4_INTERFACE *Interface
+ );
+
+EFI_STATUS
+Ip4ReceiveFrame (
+ IN IP4_INTERFACE *Interface,
+ IN IP4_PROTOCOL *IpInstance, OPTIONAL
+ IN IP4_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c
new file mode 100644
index 0000000000..135efa62ce
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c
@@ -0,0 +1,629 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Igmp.c
+
+Abstract:
+
+ This file implements the RFC2236: IGMP v2
+
+
+**/
+
+#include "Ip4Impl.h"
+
+//
+// Route Alert option in IGMP report to direct routers to
+// examine the packet more closely.
+//
+UINT32 mRouteAlertOption = 0x00000494;
+
+
+/**
+ Init the IGMP control data of the IP4 service instance, configure
+ MNP to receive ALL SYSTEM multicast.
+
+ @param IpSb The IP4 service whose IGMP is to be initialized.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.
+ @retval Others Failed to initialize the IGMP of IpSb.
+ @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.
+
+**/
+EFI_STATUS
+Ip4InitIgmp (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ IGMP_GROUP *Group;
+ EFI_STATUS Status;
+
+ IgmpCtrl = &IpSb->IgmpCtrl;
+
+ //
+ // Configure MNP to receive ALL_SYSTEM multicast
+ //
+ Group = NetAllocatePool (sizeof (IGMP_GROUP));
+
+ if (Group == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Mnp = IpSb->Mnp;
+
+ Group->Address = IP4_ALLSYSTEM_ADDRESS;
+ Group->RefCnt = 1;
+ Group->DelayTime = 0;
+ Group->ReportByUs = FALSE;
+
+ Status = Ip4GetMulticastMac (Mnp, IP4_ALLSYSTEM_ADDRESS, &Group->Mac);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ NetListInsertHead (&IgmpCtrl->Groups, &Group->Link);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ NetFreePool (Group);
+ return Status;
+}
+
+
+/**
+ Find the IGMP_GROUP structure which contains the status of multicast
+ group Address in this IGMP control block
+
+ @param IgmpCtrl The IGMP control block to search from
+ @param Address The multicast address to search
+
+ @return NULL if the multicast address isn't in the IGMP control block. Otherwise
+ @return the point to the IGMP_GROUP which contains the status of multicast group
+ @return for Address.
+
+**/
+IGMP_GROUP *
+Ip4FindGroup (
+ IN IGMP_SERVICE_DATA *IgmpCtrl,
+ IN IP4_ADDR Address
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IGMP_GROUP *Group;
+
+ NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
+
+ if (Group->Address == Address) {
+ return Group;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Count the number of IP4 multicast groups that are mapped to the
+ same MAC address. Several IP4 multicast address may be mapped to
+ the same MAC address.
+
+ @param IgmpCtrl The IGMP control block to search in
+ @param Mac The MAC address to search
+
+ @return The number of the IP4 multicast group that mapped to the same
+ @return multicast group Mac.
+
+**/
+INTN
+Ip4FindMac (
+ IN IGMP_SERVICE_DATA *IgmpCtrl,
+ IN EFI_MAC_ADDRESS *Mac
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IGMP_GROUP *Group;
+ INTN Count;
+
+ Count = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
+
+ if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {
+ Count++;
+ }
+ }
+
+ return Count;
+}
+
+
+/**
+ Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.
+
+ @param IpSb The IP4 service instance that requests the
+ transmission
+ @param Dst The destinaton to send to
+ @param Type The IGMP message type, such as IGMP v2 membership
+ report
+ @param Group The group address in the IGMP message head.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message
+ @retval EFI_SUCCESS The IGMP message is successfully send
+ @retval Others Failed to send the IGMP message.
+
+**/
+EFI_STATUS
+Ip4SendIgmpMessage (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Dst,
+ IN UINT8 Type,
+ IN IP4_ADDR Group
+ )
+{
+ IP4_HEAD Head;
+ NET_BUF *Packet;
+ IGMP_HEAD *Igmp;
+
+ //
+ // Allocate a net buffer to hold the message
+ //
+ Packet = NetbufAlloc (IP4_MAX_HEADLEN + sizeof (IGMP_HEAD));
+
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Fill in the IGMP and IP header, then transmit the message
+ //
+ NetbufReserve (Packet, IP4_MAX_HEADLEN);
+
+ Igmp = (IGMP_HEAD *) NetbufAllocSpace (Packet, sizeof (IGMP_HEAD), FALSE);
+
+ Igmp->Type = Type;
+ Igmp->MaxRespTime = 0;
+ Igmp->Checksum = 0;
+ Igmp->Group = HTONL (Group);
+ Igmp->Checksum = ~NetblockChecksum ((UINT8 *) Igmp, sizeof (IGMP_HEAD));
+
+ Head.Tos = 0;
+ Head.Protocol = IP4_PROTO_IGMP;
+ Head.Ttl = 1;
+ Head.Fragment = 0;
+ Head.Dst = Dst;
+ Head.Src = IP4_ALLZERO_ADDRESS;
+
+ return Ip4Output (
+ IpSb,
+ NULL,
+ Packet,
+ &Head,
+ (UINT8 *) &mRouteAlertOption,
+ sizeof (UINT32),
+ IP4_ALLZERO_ADDRESS,
+ Ip4SysPacketSent,
+ NULL
+ );
+}
+
+
+/**
+ Send an IGMP membership report. Depends on whether the server is
+ v1 or v2, it will send either a V1 or V2 membership report.
+
+ @param IpSb The IP4 service instance that requests the
+ transmission.
+ @param Group The group address to report
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message
+ @retval EFI_SUCCESS The IGMP report message is successfully send
+ @retval Others Failed to send the report.
+
+**/
+EFI_STATUS
+Ip4SendIgmpReport (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Group
+ )
+{
+ if (IpSb->IgmpCtrl.Igmpv1QuerySeen != 0) {
+ return Ip4SendIgmpMessage (IpSb, Group, IGMP_V1_MEMBERSHIP_REPORT, Group);
+ } else {
+ return Ip4SendIgmpMessage (IpSb, Group, IGMP_V2_MEMBERSHIP_REPORT, Group);
+ }
+}
+
+
+/**
+ Join the multicast group on behavior of this IP4 child
+
+ @param IpInstance The IP4 child that wants to join the group
+ @param Address The group to join
+
+ @retval EFI_SUCCESS Successfully join the multicast group
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources
+ @retval Others Failed to join the multicast group.
+
+**/
+EFI_STATUS
+Ip4JoinGroup (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_ADDR Address
+ )
+{
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ IP4_SERVICE *IpSb;
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ IGMP_GROUP *Group;
+ EFI_STATUS Status;
+
+ IpSb = IpInstance->Service;
+ IgmpCtrl = &IpSb->IgmpCtrl;
+ Mnp = IpSb->Mnp;
+
+ //
+ // If the IP service already is a member in the group, just
+ // increase the refernce count and return.
+ //
+ Group = Ip4FindGroup (IgmpCtrl, Address);
+
+ if (Group != NULL) {
+ Group->RefCnt++;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,
+ // send a report, then direct MNP to receive the multicast.
+ //
+ Group = NetAllocatePool (sizeof (IGMP_GROUP));
+
+ if (Group == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Group->Address = Address;
+ Group->RefCnt = 1;
+ Group->DelayTime = IGMP_UNSOLICIATED_REPORT;
+ Group->ReportByUs = TRUE;
+
+ Status = Ip4GetMulticastMac (Mnp, Address, &Group->Mac);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4SendIgmpReport (IpSb, Address);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ NetListInsertHead (&IgmpCtrl->Groups, &Group->Link);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ NetFreePool (Group);
+ return Status;
+}
+
+
+/**
+ Leave the IP4 multicast group on behavior of IpInstance.
+
+ @param IpInstance The IP4 child that wants to leave the group
+ address
+ @param Address The group address to leave
+
+ @retval EFI_NOT_FOUND The IP4 service instance isn't in the group
+ @retval EFI_SUCCESS Successfully leave the multicast group.
+ @retval Others Failed to leave the multicast group.
+
+**/
+EFI_STATUS
+Ip4LeaveGroup (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_ADDR Address
+ )
+{
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ IP4_SERVICE *IpSb;
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ IGMP_GROUP *Group;
+ EFI_STATUS Status;
+
+ IpSb = IpInstance->Service;
+ IgmpCtrl = &IpSb->IgmpCtrl;
+ Mnp = IpSb->Mnp;
+
+ Group = Ip4FindGroup (IgmpCtrl, Address);
+
+ if (Group == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If more than one instance is in the group, decrease
+ // the RefCnt then return.
+ //
+ if (--Group->RefCnt > 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // If multiple IP4 group addresses are mapped to the same
+ // multicast MAC address, don't configure the MNP to leave
+ // the MAC.
+ //
+ if (Ip4FindMac (IgmpCtrl, &Group->Mac) == 1) {
+ Status = Mnp->Groups (Mnp, FALSE, &Group->Mac);
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ return Status;
+ }
+ }
+
+ //
+ // Send a leave report if the membership is reported by us
+ // and we are talking IGMPv2.
+ //
+ if (Group->ReportByUs && !IgmpCtrl->Igmpv1QuerySeen) {
+ Ip4SendIgmpMessage (IpSb, IP4_ALLROUTER_ADDRESS, IGMP_LEAVE_GROUP, Group->Address);
+ }
+
+ NetListRemoveEntry (&Group->Link);
+ NetFreePool (Group);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Handle the received IGMP message for the IP4 service instance.
+
+ @param IpSb The IP4 service instance that received the message
+ @param Head The IP4 header of the received message
+ @param Packet The IGMP message, without IP4 header
+
+ @retval EFI_INVALID_PARAMETER The IGMP message is malformated.
+ @retval EFI_SUCCESS The IGMP message is successfully processed.
+
+**/
+EFI_STATUS
+Ip4IgmpHandle (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ IGMP_HEAD Igmp;
+ IGMP_GROUP *Group;
+ IP4_ADDR Address;
+ NET_LIST_ENTRY *Entry;
+
+ IgmpCtrl = &IpSb->IgmpCtrl;
+
+ //
+ // Must checksum over the whole packet, later IGMP version
+ // may employ message longer than 8 bytes. IP's header has
+ // already been trimmed off.
+ //
+ if ((Packet->TotalSize < sizeof (Igmp)) || (NetbufChecksum (Packet) != 0)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Copy the packet in case it is fragmented
+ //
+ NetbufCopy (Packet, 0, sizeof (IGMP_HEAD), (UINT8 *)&Igmp);
+
+ switch (Igmp.Type) {
+ case IGMP_MEMBERSHIP_QUERY:
+ //
+ // If MaxRespTIme is zero, it is most likely that we are
+ // talking to a V1 router
+ //
+ if (Igmp.MaxRespTime == 0) {
+ IgmpCtrl->Igmpv1QuerySeen = IGMP_V1ROUTER_PRESENT;
+ Igmp.MaxRespTime = 100;
+ }
+
+ //
+ // Igmp is ticking once per second but MaxRespTime is in
+ // the unit of 100ms.
+ //
+ Igmp.MaxRespTime /= 10;
+ Address = NTOHL (Igmp.Group);
+
+ if (Address == IP4_ALLSYSTEM_ADDRESS) {
+ break;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
+
+ //
+ // If address is all zero, all the memberships will be reported.
+ // otherwise only one is reported.
+ //
+ if ((Address == IP4_ALLZERO_ADDRESS) || (Address == Group->Address)) {
+ //
+ // If the timer is pending, only update it if the time left
+ // is longer than the MaxRespTime. TODO: randomize the DelayTime.
+ //
+ if ((Group->DelayTime == 0) || (Group->DelayTime > Igmp.MaxRespTime)) {
+ Group->DelayTime = NET_MAX (1, Igmp.MaxRespTime);
+ }
+ }
+ }
+
+ break;
+
+ case IGMP_V1_MEMBERSHIP_REPORT:
+ case IGMP_V2_MEMBERSHIP_REPORT:
+ Address = NTOHL (Igmp.Group);
+ Group = Ip4FindGroup (IgmpCtrl, Address);
+
+ if ((Group != NULL) && (Group->DelayTime > 0)) {
+ Group->DelayTime = 0;
+ Group->ReportByUs = FALSE;
+ }
+
+ break;
+ }
+
+ NetbufFree (Packet);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The periodical timer function for IGMP. It does the following
+ things:
+ 1. Decrease the Igmpv1QuerySeen to make it possible to refresh
+ the IGMP server type.
+ 2. Decrease the report timer for each IGMP group in "delaying
+ member" state.
+
+ @param IpSb The IP4 service instance that is ticking
+
+ @return None
+
+**/
+VOID
+Ip4IgmpTicking (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ IGMP_SERVICE_DATA *IgmpCtrl;
+ NET_LIST_ENTRY *Entry;
+ IGMP_GROUP *Group;
+
+ IgmpCtrl = &IpSb->IgmpCtrl;
+
+ if (IgmpCtrl->Igmpv1QuerySeen > 0) {
+ IgmpCtrl->Igmpv1QuerySeen--;
+ }
+
+ //
+ // Decrease the report timer for each IGMP group in "delaying member"
+ //
+ NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
+ ASSERT (Group->DelayTime >= 0);
+
+ if (Group->DelayTime > 0) {
+ Group->DelayTime--;
+
+ if (Group->DelayTime == 0) {
+ Ip4SendIgmpReport (IpSb, Group->Address);
+ Group->ReportByUs = TRUE;
+ }
+ }
+ }
+}
+
+
+/**
+ Add a group address to the array of group addresses.
+ The caller should make sure that no duplicated address
+ existed in the array. Although the function doesn't
+ assume the byte order of the both Source and Addr, the
+ network byte order is used by the caller.
+
+ @param Source The array of group addresses to add to
+ @param Count The number of group addresses in the Source
+ @param Addr The IP4 multicast address to add
+
+ @return NULL if failed to allocate memory for the new groups,
+ @return otherwise the new combined group addresses.
+
+**/
+IP4_ADDR *
+Ip4CombineGroups (
+ IN IP4_ADDR *Source,
+ IN UINT32 Count,
+ IN IP4_ADDR Addr
+ )
+{
+ IP4_ADDR *Groups;
+
+ Groups = NetAllocatePool (sizeof (IP4_ADDR) * (Count + 1));
+
+ if (Groups == NULL) {
+ return NULL;
+ }
+
+ NetCopyMem (Groups, Source, Count * sizeof (IP4_ADDR));
+ Groups[Count] = Addr;
+
+ return Groups;
+}
+
+
+/**
+ Remove a group address frome the array of group addresses.
+ Although the function doesn't assume the byte order of the
+ both Groups and Addr, the network byte order is used by
+ the caller.
+
+ @param Groups The array of group addresses to remove from
+ @param Count The number of group addresses in the Groups
+ @param Addr The IP4 multicast address to remove
+
+ @return The nubmer of group addresses in the Groups after remove.
+ @return It is Count if the Addr isn't in the Groups.
+
+**/
+INTN
+Ip4RemoveGroupAddr (
+ IN IP4_ADDR *Groups,
+ IN UINT32 Count,
+ IN IP4_ADDR Addr
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < Count; Index++) {
+ if (Groups[Index] == Addr) {
+ break;
+ }
+ }
+
+ while (Index < Count - 1) {
+ Groups[Index] = Groups[Index + 1];
+ Index++;
+ }
+
+ return Index;
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h
new file mode 100644
index 0000000000..f7cb92c23f
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h
@@ -0,0 +1,120 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Igmp.h
+
+Abstract:
+
+
+**/
+
+#ifndef __EFI_IP4_IGMP_H__
+#define __EFI_IP4_IGMP_H__
+
+#pragma pack(1)
+typedef struct {
+ UINT8 Type;
+ UINT8 MaxRespTime;
+ UINT16 Checksum;
+ IP4_ADDR Group;
+} IGMP_HEAD;
+#pragma pack()
+
+//
+// The status of multicast group. It isn't necessary to maintain
+// explicit state of host state diagram. A group with non-zero
+// DelayTime is in "delaying member" state. otherwise, it is in
+// "idle member" state.
+//
+typedef struct {
+ NET_LIST_ENTRY Link;
+ INTN RefCnt;
+ IP4_ADDR Address;
+ INTN DelayTime;
+ BOOLEAN ReportByUs;
+ EFI_MAC_ADDRESS Mac;
+} IGMP_GROUP;
+
+//
+// The IGMP status. Each IP4 service instance has a IGMP_SERVICE_DATA
+// attached. The Igmpv1QuerySeen remember whether the server on this
+// connected network is v1 or v2.
+//
+typedef struct {
+ INTN Igmpv1QuerySeen;
+ NET_LIST_ENTRY Groups;
+} IGMP_SERVICE_DATA;
+
+enum {
+ //
+ // IGMP message type
+ //
+ IGMP_MEMBERSHIP_QUERY = 0x11,
+ IGMP_V1_MEMBERSHIP_REPORT = 0x12,
+ IGMP_V2_MEMBERSHIP_REPORT = 0x16,
+ IGMP_LEAVE_GROUP = 0x17,
+
+ IGMP_V1ROUTER_PRESENT = 400,
+ IGMP_UNSOLICIATED_REPORT = 10,
+};
+
+EFI_STATUS
+Ip4InitIgmp (
+ IN IP4_SERVICE *IpService
+ );
+
+EFI_STATUS
+Ip4JoinGroup (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_ADDR Address
+ );
+
+EFI_STATUS
+Ip4LeaveGroup (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_ADDR Address
+ );
+
+EFI_STATUS
+Ip4IgmpHandle (
+ IN IP4_SERVICE *IpService,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ );
+
+VOID
+Ip4IgmpTicking (
+ IN IP4_SERVICE *IpService
+ );
+
+IP4_ADDR *
+Ip4CombineGroups (
+ IN IP4_ADDR *SourceGroups,
+ IN UINT32 Count,
+ IN IP4_ADDR Addr
+ );
+
+INTN
+Ip4RemoveGroupAddr (
+ IN IP4_ADDR *Group,
+ IN UINT32 GroupCnt,
+ IN IP4_ADDR Addr
+ );
+
+IGMP_GROUP *
+Ip4FindGroup (
+ IN IGMP_SERVICE_DATA *IgmpCtrl,
+ IN IP4_ADDR Address
+ );
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c
new file mode 100644
index 0000000000..824524d508
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c
@@ -0,0 +1,2022 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Impl.c
+
+Abstract:
+
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Get the IP child's current operational data. This can
+ all be used to get the underlying MNP and SNP data.
+
+ @param This The IP4 protocol instance
+ @param Ip4ModeData The IP4 operation data
+ @param MnpConfigData The MNP configure data
+ @param SnpModeData The SNP operation data
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid because This == NULL
+ @retval EFI_SUCCESS The operational parameter is returned.
+ @retval Others Failed to retrieve the IP4 route table.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiIp4GetModeData (
+ IN CONST EFI_IP4_PROTOCOL *This,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData, OPTIONAL
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData, OPTIONAL
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ IP4_SERVICE *IpSb;
+ EFI_IP4_CONFIG_DATA *Config;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ IP4_ADDR Ip;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (Ip4ModeData != NULL) {
+ //
+ // IsStarted is "whether the EfiIp4Configure has been called".
+ // IsConfigured is "whether the station address has been configured"
+ //
+ Ip4ModeData->IsStarted = (BOOLEAN)(IpInstance->State == IP4_STATE_CONFIGED);
+ CopyMem (&Ip4ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP4_CONFIG_DATA));
+ Ip4ModeData->IsConfigured = FALSE;
+
+ Ip4ModeData->GroupCount = IpInstance->GroupCount;
+ Ip4ModeData->GroupTable = (EFI_IPv4_ADDRESS *) IpInstance->Groups;
+
+ Ip4ModeData->IcmpTypeCount = 23;
+ Ip4ModeData->IcmpTypeList = mIp4SupportedIcmp;
+
+ Ip4ModeData->RouteTable = NULL;
+ Ip4ModeData->RouteCount = 0;
+
+ //
+ // return the current station address for this IP child. So,
+ // the user can get the default address through this. Some
+ // application wants to know it station address even it is
+ // using the default one, such as a ftp server.
+ //
+ if (Ip4ModeData->IsStarted) {
+ Config = &Ip4ModeData->ConfigData;
+
+ Ip = HTONL (IpInstance->Interface->Ip);
+ NetCopyMem (&Config->StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (IpInstance->Interface->SubnetMask);
+ NetCopyMem (&Config->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip4ModeData->IsConfigured = IpInstance->Interface->Configured;
+
+ //
+ // Build a EFI route table for user from the internal route table.
+ //
+ Status = Ip4BuildEfiRouteTable (IpInstance);
+
+ if (EFI_ERROR (Status)) {
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+ }
+
+ Ip4ModeData->RouteTable = IpInstance->EfiRouteTable;
+ Ip4ModeData->RouteCount = IpInstance->EfiRouteCount;
+ }
+ }
+
+ if (MnpConfigData != NULL) {
+ CopyMem (MnpConfigData, &IpSb->MnpConfigData, sizeof (EFI_MANAGED_NETWORK_CONFIG_DATA));
+ }
+
+ if (SnpModeData != NULL) {
+ CopyMem (SnpModeData, &IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));
+ }
+
+ NET_RESTORE_TPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Config the MNP parameter used by IP. The IP driver use one MNP
+ child to transmit/receive frames. By default, it configures MNP
+ to receive unicast/multicast/broadcast. And it will enable/disable
+ the promiscous receive according to whether there is IP child
+ enable that or not. If Force isn't false, it will iterate through
+ all the IP children to check whether the promiscuous receive
+ setting has been changed. If it hasn't been changed, it won't
+ reconfigure the MNP. If Force is true, the MNP is configured no
+ matter whether that is changed or not.
+
+ @param IpSb The IP4 service instance that is to be changed.
+ @param Force Force the configuration or not.
+
+ @retval EFI_SUCCESS The MNP is successfully configured/reconfigured.
+ @retval Others Configuration failed.
+
+**/
+EFI_STATUS
+Ip4ServiceConfigMnp (
+ IN IP4_SERVICE *IpSb,
+ IN BOOLEAN Force
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *ProtoEntry;
+ IP4_INTERFACE *IpIf;
+ IP4_PROTOCOL *IpInstance;
+ BOOLEAN Reconfig;
+ BOOLEAN PromiscReceive;
+ EFI_STATUS Status;
+
+ Reconfig = FALSE;
+ PromiscReceive = FALSE;
+
+ if (!Force) {
+ //
+ // Iterate through the IP children to check whether promiscuous
+ // receive setting has been changed. Update the interface's receive
+ // filter also.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+ IpIf->PromiscRecv = FALSE;
+
+ NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) {
+ IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP4_PROTOCOL, AddrLink);
+
+ if (IpInstance->ConfigData.AcceptPromiscuous) {
+ IpIf->PromiscRecv = TRUE;
+ PromiscReceive = TRUE;
+ }
+ }
+ }
+
+ //
+ // If promiscuous receive isn't changed, it isn't necessary to reconfigure.
+ //
+ if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) {
+ return EFI_SUCCESS;
+ }
+
+ Reconfig = TRUE;
+ IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive;
+ }
+
+ Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData);
+
+ //
+ // recover the original configuration if failed to set the configure.
+ //
+ if (EFI_ERROR (Status) && Reconfig) {
+ IpSb->MnpConfigData.EnablePromiscuousReceive = !PromiscReceive;
+ }
+
+ return Status;
+}
+
+
+/**
+ The event handle for IP4 auto configuration. If IP is asked
+ to reconfigure the default address. The original default
+ interface and route table are removed as the default. If there
+ is active IP children using the default address, the interface
+ will remain valid until all the children have freed their
+ references. If IP is signalled when auto configuration is done,
+ it will configure the default interface and default route table
+ with the configuration information retrieved by IP4_CONFIGURE.
+
+ @param Event The event that is signalled.
+ @param Context The IP4 service binding instance.
+
+ @return None
+
+**/
+VOID
+EFIAPI
+Ip4AutoConfigCallBack (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_IP4_CONFIG_PROTOCOL *Ip4Config;
+ EFI_IP4_IPCONFIG_DATA *Data;
+ EFI_IP4_ROUTE_TABLE *RouteEntry;
+ IP4_SERVICE *IpSb;
+ IP4_ROUTE_TABLE *RouteTable;
+ IP4_INTERFACE *IpIf;
+ EFI_STATUS Status;
+ UINTN Len;
+ UINT32 Index;
+
+ IpSb = (IP4_SERVICE *) Context;
+ NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE);
+
+ Ip4Config = IpSb->Ip4Config;
+
+ //
+ // IP is asked to do the reconfiguration. If the default interface
+ // has been configured, release the default interface and route
+ // table, then create a new one. If there are some IP children
+ // using it, the interface won't be physically freed until all the
+ // children have released their reference to it. Also remember to
+ // restart the receive on the default address. IP4 driver only receive
+ // frames on the default address, and when the default interface is
+ // freed, Ip4AcceptFrame won't be informed.
+ //
+ if (Event == IpSb->ReconfigEvent) {
+
+ if (IpSb->DefaultInterface->Configured) {
+ IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image);
+
+ if (IpIf == NULL) {
+ return;
+ }
+
+ RouteTable = Ip4CreateRouteTable ();
+
+ if (RouteTable == NULL) {
+ Ip4FreeInterface (IpIf, NULL);
+ return;
+ }
+
+ Ip4CancelReceive (IpSb->DefaultInterface);
+ Ip4FreeInterface (IpSb->DefaultInterface, NULL);
+ Ip4FreeRouteTable (IpSb->DefaultRouteTable);
+
+ IpSb->DefaultInterface = IpIf;
+ NetListInsertHead (&IpSb->Interfaces, &IpIf->Link);
+
+ IpSb->DefaultRouteTable = RouteTable;
+ Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb);
+ }
+
+ Ip4Config->Stop (Ip4Config);
+ Ip4Config->Start (Ip4Config, IpSb->DoneEvent, IpSb->ReconfigEvent);
+ return ;
+ }
+
+ //
+ // Get the configure data in two steps: get the length then the data.
+ //
+ Len = 0;
+
+ if (Ip4Config->GetData (Ip4Config, &Len, NULL) != EFI_BUFFER_TOO_SMALL) {
+ return ;
+ }
+
+ Data = NetAllocatePool (Len);
+
+ if (Data == NULL) {
+ return ;
+ }
+
+ Status = Ip4Config->GetData (Ip4Config, &Len, Data);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ IpIf = IpSb->DefaultInterface;
+
+ //
+ // If the default address has been configured don't change it.
+ // This is unlikely to happen if EFI_IP4_CONFIG protocol has
+ // informed us to reconfigure each time it wants to change the
+ // configuration parameters.
+ //
+ if (IpIf->Configured) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Set the default interface's address, then add a directed
+ // route for it, that is, the route whose nexthop is zero.
+ //
+ Status = Ip4SetAddress (
+ IpIf,
+ EFI_NTOHL (Data->StationAddress),
+ EFI_NTOHL (Data->SubnetMask)
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Ip4AddRoute (
+ IpSb->DefaultRouteTable,
+ EFI_NTOHL (Data->StationAddress),
+ EFI_NTOHL (Data->SubnetMask),
+ IP4_ALLZERO_ADDRESS
+ );
+
+ //
+ // Add routes returned by EFI_IP4_CONFIG protocol.
+ //
+ for (Index = 0; Index < Data->RouteTableSize; Index++) {
+ RouteEntry = &Data->RouteTable[Index];
+
+ Ip4AddRoute (
+ IpSb->DefaultRouteTable,
+ EFI_NTOHL (RouteEntry->SubnetAddress),
+ EFI_NTOHL (RouteEntry->SubnetMask),
+ EFI_NTOHL (RouteEntry->GatewayAddress)
+ );
+ }
+
+ IpSb->State = IP4_SERVICE_CONFIGED;
+
+ Ip4SetVariableData (IpSb);
+
+ON_EXIT:
+ NetFreePool (Data);
+}
+
+
+/**
+ Start the auto configuration for this IP service instance.
+ It will locates the EFI_IP4_CONFIG_PROTOCOL, then start the
+ auto configuration.
+
+ @param IpSb The IP4 service instance to configure
+
+ @retval EFI_SUCCESS The auto configuration is successfull started
+ @retval Others Failed to start auto configuration.
+
+**/
+EFI_STATUS
+Ip4StartAutoConfig (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ EFI_IP4_CONFIG_PROTOCOL *Ip4Config;
+ EFI_STATUS Status;
+
+ if (IpSb->State > IP4_SERVICE_UNSTARTED) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Create the DoneEvent and ReconfigEvent to call EFI_IP4_CONFIG
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ NET_TPL_LOCK,
+ Ip4AutoConfigCallBack,
+ IpSb,
+ &IpSb->DoneEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ip4AutoConfigCallBack,
+ IpSb,
+ &IpSb->ReconfigEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto CLOSE_DONE_EVENT;
+ }
+
+ //
+ // Open the EFI_IP4_CONFIG protocol then start auto configure
+ //
+ Status = gBS->OpenProtocol (
+ IpSb->Controller,
+ &gEfiIp4ConfigProtocolGuid,
+ (VOID **) &Ip4Config,
+ IpSb->Image,
+ IpSb->Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE
+ );
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_UNSUPPORTED;
+ goto CLOSE_RECONFIG_EVENT;
+ }
+
+ Status = Ip4Config->Start (Ip4Config, IpSb->DoneEvent, IpSb->ReconfigEvent);
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ IpSb->Controller,
+ &gEfiIp4ConfigProtocolGuid,
+ IpSb->Image,
+ IpSb->Controller
+ );
+
+ goto CLOSE_RECONFIG_EVENT;
+ }
+
+ IpSb->Ip4Config = Ip4Config;
+ IpSb->State = IP4_SERVICE_STARTED;
+ return Status;
+
+CLOSE_RECONFIG_EVENT:
+ gBS->CloseEvent (IpSb->ReconfigEvent);
+ IpSb->ReconfigEvent = NULL;
+
+CLOSE_DONE_EVENT:
+ gBS->CloseEvent (IpSb->DoneEvent);
+ IpSb->DoneEvent = NULL;
+
+ return Status;
+}
+
+
+/**
+ Intiialize the IP4_PROTOCOL structure to the unconfigured states.
+
+ @param IpSb The IP4 service instance.
+ @param IpInstance The IP4 child instance.
+
+ @return None
+
+**/
+VOID
+Ip4InitProtocol (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_PROTOCOL *IpInstance
+ )
+{
+ ASSERT ((IpSb != NULL) && (IpInstance != NULL));
+
+ NetZeroMem (IpInstance, sizeof (IP4_PROTOCOL));
+
+ IpInstance->Signature = IP4_PROTOCOL_SIGNATURE;
+ CopyMem (&IpInstance->Ip4Proto, &mEfiIp4ProtocolTemplete, sizeof (EFI_IP4_PROTOCOL));
+ IpInstance->State = IP4_STATE_UNCONFIGED;
+ IpInstance->Service = IpSb;
+
+ NetListInit (&IpInstance->Link);
+ NetMapInit (&IpInstance->RxTokens);
+ NetMapInit (&IpInstance->TxTokens);
+ NetListInit (&IpInstance->Received);
+ NetListInit (&IpInstance->Delivered);
+ NetListInit (&IpInstance->AddrLink);
+
+ NET_RECYCLE_LOCK_INIT (&IpInstance->RecycleLock);
+}
+
+
+/**
+ Configure the IP4 child. If the child is already configured,
+ change the configuration parameter. Otherwise configure it
+ for the first time. The caller should validate the configuration
+ before deliver them to it. It also don't do configure NULL.
+
+ @param IpInstance The IP4 child to configure.
+ @param Config The configure data.
+
+ @retval EFI_SUCCESS The IP4 child is successfully configured.
+ @retval EFI_DEVICE_ERROR Failed to free the pending transive or to
+ configure underlying MNP or other errors.
+ @retval EFI_NO_MAPPING The IP4 child is configured to use default
+ address, but the default address hasn't been
+ configured. The IP4 child doesn't need to be
+ reconfigured when default address is configured.
+
+**/
+EFI_STATUS
+Ip4ConfigProtocol (
+ IN IP4_PROTOCOL *IpInstance,
+ IN EFI_IP4_CONFIG_DATA *Config
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_INTERFACE *IpIf;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+ IP4_ADDR Netmask;
+
+ IpSb = IpInstance->Service;
+
+ //
+ // User is changing packet filters. It must be stopped
+ // before the station address can be changed.
+ //
+ if (IpInstance->State == IP4_STATE_CONFIGED) {
+ //
+ // Cancel all the pending transmit/receive from upper layer
+ //
+ Status = Ip4Cancel (IpInstance, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (&IpInstance->ConfigData, Config, sizeof (EFI_IP4_CONFIG_DATA));
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Configure a fresh IP4 protocol instance. Create a route table.
+ // Each IP child has its own route table, which may point to the
+ // default table if it is using default address.
+ //
+ Status = EFI_OUT_OF_RESOURCES;
+ IpInstance->RouteTable = Ip4CreateRouteTable ();
+
+ if (IpInstance->RouteTable == NULL) {
+ return Status;
+ }
+
+ //
+ // Set up the interface.
+ //
+ NetCopyMem (&Ip, &Config->StationAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR));
+
+ Ip = NTOHL (Ip);
+ Netmask = NTOHL (Netmask);
+
+ if (!Config->UseDefaultAddress) {
+ //
+ // Find whether there is already an interface with the same
+ // station address. All the instances with the same station
+ // address shares one interface.
+ //
+ IpIf = Ip4FindStationAddress (IpSb, Ip, Netmask);
+
+ if (IpIf != NULL) {
+ NET_GET_REF (IpIf);
+
+ } else {
+ IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image);
+
+ if (IpIf == NULL) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip4SetAddress (IpIf, Ip, Netmask);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ Ip4FreeInterface (IpIf, IpInstance);
+ goto ON_ERROR;
+ }
+
+ NetListInsertTail (&IpSb->Interfaces, &IpIf->Link);
+ }
+
+ //
+ // Add a route to this connected network in the route table
+ //
+ Ip4AddRoute (IpInstance->RouteTable, Ip, Netmask, IP4_ALLZERO_ADDRESS);
+
+ } else {
+ //
+ // Use the default address. If the default configuration hasn't
+ // been started, start it.
+ //
+ if (IpSb->State == IP4_SERVICE_UNSTARTED) {
+ Status = Ip4StartAutoConfig (IpSb);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ IpIf = IpSb->DefaultInterface;
+ NET_GET_REF (IpSb->DefaultInterface);
+
+ //
+ // If default address is used, so is the default route table.
+ // Any route set by the instance has the precedence over the
+ // routes in the default route table. Link the default table
+ // after the instance's table. Routing will search the local
+ // table first.
+ //
+ NET_GET_REF (IpSb->DefaultRouteTable);
+ IpInstance->RouteTable->Next = IpSb->DefaultRouteTable;
+ }
+
+ IpInstance->Interface = IpIf;
+ NetListInsertTail (&IpIf->IpInstances, &IpInstance->AddrLink);
+
+ CopyMem (&IpInstance->ConfigData, Config, sizeof (EFI_IP4_CONFIG_DATA));
+ IpInstance->State = IP4_STATE_CONFIGED;
+
+ //
+ // Although EFI_NO_MAPPING is an error code, the IP child has been
+ // successfully configured and doesn't need reconfiguration when
+ // default address is acquired.
+ //
+ if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ return EFI_NO_MAPPING;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip4FreeRouteTable (IpInstance->RouteTable);
+ IpInstance->RouteTable = NULL;
+ return Status;
+}
+
+
+/**
+ Clean up the IP4 child, release all the resources used by it.
+
+ @param IpInstance The IP4 child to clean up.
+
+ @retval EFI_SUCCESS The IP4 child is cleaned up
+ @retval EFI_DEVICE_ERROR Some resources failed to be released
+
+**/
+EFI_STATUS
+Ip4CleanProtocol (
+ IN IP4_PROTOCOL *IpInstance
+ )
+{
+ if (EFI_ERROR (Ip4Cancel (IpInstance, NULL))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (EFI_ERROR (Ip4Groups (IpInstance, FALSE, NULL))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Some packets haven't been recycled. It is because either the
+ // user forgets to recycle the packets, or because the callback
+ // hasn't been called. Just leave it alone.
+ //
+ if (!NetListIsEmpty (&IpInstance->Delivered)) {
+ ;
+ }
+
+ if (IpInstance->Interface != NULL) {
+ NetListRemoveEntry (&IpInstance->AddrLink);
+ Ip4FreeInterface (IpInstance->Interface, IpInstance);
+ IpInstance->Interface = NULL;
+ }
+
+ if (IpInstance->RouteTable != NULL) {
+ if (IpInstance->RouteTable->Next != NULL) {
+ Ip4FreeRouteTable (IpInstance->RouteTable->Next);
+ }
+
+ Ip4FreeRouteTable (IpInstance->RouteTable);
+ IpInstance->RouteTable = NULL;
+ }
+
+ if (IpInstance->EfiRouteTable != NULL) {
+ NetFreePool (IpInstance->EfiRouteTable);
+ IpInstance->EfiRouteTable = NULL;
+ IpInstance->EfiRouteCount = 0;
+ }
+
+ if (IpInstance->Groups != NULL) {
+ NetFreePool (IpInstance->Groups);
+ IpInstance->Groups = NULL;
+ IpInstance->GroupCount = 0;
+ }
+
+ NetMapClean (&IpInstance->TxTokens);
+
+ NetMapClean (&IpInstance->RxTokens);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Validate that Ip/Netmask pair is OK to be used as station
+ address. Only continuous netmasks are supported. and check
+ that StationAddress is a unicast address on the newtwork.
+
+ @param Ip The IP address to validate
+ @param Netmask The netmaks of the IP
+
+ @retval TRUE The Ip/Netmask pair is valid
+ @retval FALSE The
+
+**/
+BOOLEAN
+Ip4StationAddressValid (
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR Netmask
+ )
+{
+ IP4_ADDR NetBrdcastMask;
+ INTN Len;
+ INTN Type;
+
+ //
+ // Only support the station address with 0.0.0.0/0 to enable DHCP client.
+ //
+ if (Netmask == IP4_ALLZERO_ADDRESS) {
+ return (BOOLEAN) (Ip == IP4_ALLZERO_ADDRESS);
+ }
+
+ //
+ // Only support the continuous net masks
+ //
+ if ((Len = NetGetMaskLength (Netmask)) == IP4_MASK_NUM) {
+ return FALSE;
+ }
+
+ //
+ // Station address can't be class D or class E address
+ //
+ if ((Type = NetGetIpClass (Ip)) > IP4_ADDR_CLASSC) {
+ return FALSE;
+ }
+
+ //
+ // Station address can't be subnet broadcast/net broadcast address
+ //
+ if ((Ip == (Ip & Netmask)) || (Ip == (Ip | ~Netmask))) {
+ return FALSE;
+ }
+
+ NetBrdcastMask = mIp4AllMasks[NET_MIN (Len, Type << 3)];
+
+ if (Ip == (Ip | ~NetBrdcastMask)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Configure the EFI_IP4_PROTOCOL instance. If IpConfigData is NULL,
+ the instance is cleaned up. If the instance hasn't been configure
+ before, it will be initialized. Otherwise, the filter setting of
+ the instance is updated.
+
+ @param This The IP4 child to configure
+ @param IpConfigData The configuration to apply. If NULL, clean it up.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid
+ @retval EFI_NO_MAPPING The default address hasn't been configured and the
+ instance wants to use it.
+ @retval EFI_SUCCESS The instance is configured.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiIp4Configure (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_CONFIG_DATA *IpConfigData OPTIONAL
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_IP4_CONFIG_DATA *Current;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ BOOLEAN AddrOk;
+ IP4_ADDR IpAddress;
+ IP4_ADDR SubnetMask;
+
+ //
+ // First, validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // Validate the configuration first.
+ //
+ if (IpConfigData != NULL) {
+ //
+ // This implementation doesn't support RawData
+ //
+ if (IpConfigData->RawData) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ }
+
+
+ NetCopyMem (&IpAddress, &IpConfigData->StationAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&SubnetMask, &IpConfigData->SubnetMask, sizeof (IP4_ADDR));
+
+ IpAddress = NTOHL (IpAddress);
+ SubnetMask = NTOHL (SubnetMask);
+
+ //
+ // Check whether the station address is a valid unicast address
+ //
+ if (!IpConfigData->UseDefaultAddress) {
+ AddrOk = Ip4StationAddressValid (IpAddress, SubnetMask);
+
+ if (!AddrOk) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // User can only update packet filters when already configured.
+ // If it wants to change the station address, it must configure(NULL)
+ // the instance first.
+ //
+ if (IpInstance->State == IP4_STATE_CONFIGED) {
+ Current = &IpInstance->ConfigData;
+
+ if (Current->UseDefaultAddress != IpConfigData->UseDefaultAddress) {
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (!Current->UseDefaultAddress &&
+ (!EFI_IP4_EQUAL (Current->StationAddress, IpConfigData->StationAddress) ||
+ !EFI_IP4_EQUAL (Current->SubnetMask, IpConfigData->SubnetMask))) {
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (Current->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ return EFI_NO_MAPPING;
+ }
+ }
+ }
+
+ //
+ // Configure the instance or clean it up.
+ //
+ if (IpConfigData != NULL) {
+ Status = Ip4ConfigProtocol (IpInstance, IpConfigData);
+ } else {
+ Status = Ip4CleanProtocol (IpInstance);
+
+ //
+ // Don't change the state if it is DESTORY, consider the following
+ // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped,
+ // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED,
+ // the unload fails miserably.
+ //
+ if (IpInstance->State == IP4_STATE_CONFIGED) {
+ IpInstance->State = IP4_STATE_UNCONFIGED;
+ }
+ }
+
+ //
+ // Update the MNP's configure data. Ip4ServiceConfigMnp will check
+ // whether it is necessary to reconfigure the MNP.
+ //
+ Ip4ServiceConfigMnp (IpInstance->Service, FALSE);
+
+ //
+ // Update the variable data.
+ //
+ Ip4SetVariableData (IpInstance->Service);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+
+}
+
+
+/**
+ Change the IP4 child's multicast setting. The caller
+ should make sure that the parameters is valid.
+
+ @param IpInstance The IP4 child to change the setting.
+ @param JoinFlag TRUE to join the group, otherwise leave it
+ @param GroupAddress The target group address
+
+ @retval EFI_ALREADY_STARTED Want to join the group, but already a member of it
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
+ @retval EFI_DEVICE_ERROR Failed to set the group configuraton
+ @retval EFI_SUCCESS Successfully updated the group setting.
+ @retval EFI_NOT_FOUND Try to leave the group which it isn't a member.
+
+**/
+EFI_STATUS
+Ip4Groups (
+ IN IP4_PROTOCOL *IpInstance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL
+ )
+{
+ IP4_ADDR *Members;
+ IP4_ADDR Group;
+ UINT32 Index;
+
+ //
+ // Add it to the instance's Groups, and join the group by IGMP.
+ // IpInstance->Groups is in network byte order. IGMP operates in
+ // host byte order
+ //
+ if (JoinFlag) {
+ NetCopyMem (&Group, GroupAddress, sizeof (IP4_ADDR));
+
+ for (Index = 0; Index < IpInstance->GroupCount; Index++) {
+ if (IpInstance->Groups[Index] == Group) {
+ return EFI_ALREADY_STARTED;
+ }
+ }
+
+ Members = Ip4CombineGroups (IpInstance->Groups, IpInstance->GroupCount, Group);
+
+ if (Members == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (EFI_ERROR (Ip4JoinGroup (IpInstance, NTOHL (Group)))) {
+ NetFreePool (Members);
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (IpInstance->Groups != NULL) {
+ NetFreePool (IpInstance->Groups);
+ }
+
+ IpInstance->Groups = Members;
+ IpInstance->GroupCount++;
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Leave the group. Leave all the groups if GroupAddress is NULL.
+ // Must iterate from the end to the beginning because the GroupCount
+ // is decreamented each time an address is removed..
+ //
+ for (Index = IpInstance->GroupCount; Index > 0 ; Index--) {
+ Group = IpInstance->Groups[Index - 1];
+
+ if ((GroupAddress == NULL) || EFI_IP4_EQUAL (Group, *GroupAddress)) {
+ if (EFI_ERROR (Ip4LeaveGroup (IpInstance, NTOHL (Group)))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Ip4RemoveGroupAddr (IpInstance->Groups, IpInstance->GroupCount, Group);
+ IpInstance->GroupCount--;
+
+ if (IpInstance->GroupCount == 0) {
+ ASSERT (Index == 1);
+
+ NetFreePool (IpInstance->Groups);
+ IpInstance->Groups = NULL;
+ }
+
+ if (GroupAddress != NULL) {
+ return EFI_SUCCESS;
+ }
+ }
+ }
+
+ return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);
+}
+
+
+/**
+ Change the IP4 child's multicast setting. If JoinFlag is true,
+ the child wants to join the group. Otherwise it wants to leave
+ the group. If JoinFlag is false, and GroupAddress is NULL,
+ it will leave all the groups which is a member.
+
+ @param This The IP4 child to change the setting.
+ @param JoinFlag TRUE to join the group, otherwise leave it.
+ @param GroupAddress The target group address
+
+ @retval EFI_INVALID_PARAMETER The parameters are invalid
+ @retval EFI_SUCCESS The group setting has been changed.
+ @retval Otherwise It failed to change the setting.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiIp4Groups (
+ IN EFI_IP4_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ IP4_ADDR McastIp;
+
+ if ((This == NULL) || (JoinFlag && (GroupAddress == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (GroupAddress != NULL) {
+ NetCopyMem (&McastIp, GroupAddress, sizeof (IP4_ADDR));
+
+ if (!IP4_IS_MULTICAST (NTOHL (McastIp))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+
+ Status = Ip4Groups (IpInstance, JoinFlag, GroupAddress);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Modify the IP child's route table. Each instance has its own
+ route table.
+
+ @param This The IP4 child to modify the route
+ @param DeleteRoute TRUE to delete the route, otherwise add it
+ @param SubnetAddress The destination network
+ @param SubnetMask The destination network's mask
+ @param GatewayAddress The next hop address.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_SUCCESS The route table is successfully modified.
+ @retval Others Failed to modify the route table
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiIp4Routes (
+ IN EFI_IP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ IP4_INTERFACE *IpIf;
+ IP4_ADDR Dest;
+ IP4_ADDR Netmask;
+ IP4_ADDR Nexthop;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First, validate the parameters
+ //
+ if ((This == NULL) || (SubnetAddress == NULL) ||
+ (SubnetMask == NULL) || (GatewayAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+
+ NetCopyMem (&Dest, SubnetAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&Netmask, SubnetMask, sizeof (IP4_ADDR));
+ NetCopyMem (&Nexthop, GatewayAddress, sizeof (IP4_ADDR));
+
+ Dest = NTOHL (Dest);
+ Netmask = NTOHL (Netmask);
+ Nexthop = NTOHL (Nexthop);
+
+ IpIf = IpInstance->Interface;
+
+ if (!IP4_IS_VALID_NETMASK (Netmask)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ //
+ // the gateway address must be a unicast on the connected network if not zero.
+ //
+ if ((Nexthop != IP4_ALLZERO_ADDRESS) &&
+ (!IP4_NET_EQUAL (Nexthop, IpIf->Ip, IpIf->SubnetMask) ||
+ IP4_IS_BROADCAST (Ip4GetNetCast (Nexthop, IpIf)))) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (DeleteRoute) {
+ Status = Ip4DelRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop);
+ } else {
+ Status = Ip4AddRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop);
+ }
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Check whether the user's token or event has already
+ been enqueue on IP4's list.
+
+ @param Map The container of either user's transmit or receive
+ token.
+ @param Item Current item to check against
+ @param Context The Token to check againist.
+
+ @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP
+ @retval EFI_SUCCESS The current item isn't the same token/event as the
+ context.
+
+**/
+STATIC
+EFI_STATUS
+Ip4TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ EFI_IP4_COMPLETION_TOKEN *TokenInItem;
+
+ Token = (EFI_IP4_COMPLETION_TOKEN *) Context;
+ TokenInItem = (EFI_IP4_COMPLETION_TOKEN *) Item->Key;
+
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Validate the user's token against current station address.
+
+ @param Token User's token to validate
+ @param IpIf The IP4 child's interface.
+
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid
+ @retval EFI_BAD_BUFFER_SIZE The user's option/data is too long.
+ @retval EFI_SUCCESS The token is OK
+
+**/
+STATIC
+EFI_STATUS
+Ip4TxTokenValid (
+ IN EFI_IP4_COMPLETION_TOKEN *Token,
+ IN IP4_INTERFACE *IpIf
+ )
+{
+ EFI_IP4_TRANSMIT_DATA *TxData;
+ EFI_IP4_OVERRIDE_DATA *Override;
+ IP4_ADDR Src;
+ IP4_ADDR Gateway;
+ UINT32 Offset;
+ UINT32 Index;
+ UINT32 HeadLen;
+
+ if ((Token == NULL) || (Token->Event == NULL) || (Token->Packet.TxData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TxData = Token->Packet.TxData;
+
+ //
+ // Check the IP options: no more than 40 bytes and format is OK
+ //
+ if (TxData->OptionsLength != 0) {
+ if ((TxData->OptionsLength > 40) || (TxData->OptionsBuffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Ip4OptionIsValid (TxData->OptionsBuffer, TxData->OptionsLength, FALSE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Check the fragment table: no empty fragment, and length isn't bogus
+ //
+ if ((TxData->TotalDataLength == 0) || (TxData->FragmentCount == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Offset = TxData->TotalDataLength;
+
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+ if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||
+ (TxData->FragmentTable[Index].FragmentLength == 0)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Offset -= TxData->FragmentTable[Index].FragmentLength;
+ }
+
+ if (Offset != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check the source and gateway: they must be a valid unicast.
+ // Gateway must also be on the connected network.
+ //
+ if (TxData->OverrideData) {
+ Override = TxData->OverrideData;
+
+ NetCopyMem (&Src, &Override->SourceAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&Gateway, &Override->GatewayAddress, sizeof (IP4_ADDR));
+
+ Src = NTOHL (Src);
+ Gateway = NTOHL (Gateway);
+
+ if ((NetGetIpClass (Src) > IP4_ADDR_CLASSC) ||
+ (Src == IP4_ALLONE_ADDRESS) ||
+ IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If gateway isn't zero, it must be a unicast address, and
+ // on the connected network.
+ //
+ if ((Gateway != IP4_ALLZERO_ADDRESS) &&
+ ((NetGetIpClass (Gateway) > IP4_ADDR_CLASSC) ||
+ !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask) ||
+ IP4_IS_BROADCAST (Ip4GetNetCast (Gateway, IpIf)))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Check the packet length: Head length and packet length all has a limit
+ //
+ HeadLen = sizeof (IP4_HEAD) + ((TxData->OptionsLength + 3) &~0x03);
+
+ if ((HeadLen > IP4_MAX_HEADLEN) ||
+ (TxData->TotalDataLength + HeadLen > IP4_MAX_PACKET_SIZE)) {
+
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The callback function for the net buffer which wraps the user's
+ transmit token. Although it seems this function is pretty simple,
+ there are some subtle things.
+ When user requests the IP to transmit a packet by passing it a
+ token, the token is wrapped in an IP4_TXTOKEN_WRAP and the data
+ is wrapped in an net buffer. the net buffer's Free function is
+ set to Ip4FreeTxToken. The Token and token wrap are added to the
+ IP child's TxToken map. Then the buffer is passed to Ip4Output for
+ transmission. If something error happened before that, the buffer
+ is freed, which in turn will free the token wrap. The wrap may
+ have been added to the TxToken map or not, and the user's event
+ shouldn't be fired because we are still in the EfiIp4Transmit. If
+ the buffer has been sent by Ip4Output, it should be removed from
+ the TxToken map and user's event signaled. The token wrap and buffer
+ are bound together. Check the comments in Ip4Output for information
+ about IP fragmentation.
+
+ @param Context The token's wrap
+
+ @return None
+
+**/
+STATIC
+VOID
+Ip4FreeTxToken (
+ IN VOID *Context
+ )
+{
+ IP4_TXTOKEN_WRAP *Wrap;
+ NET_MAP_ITEM *Item;
+
+ Wrap = (IP4_TXTOKEN_WRAP *) Context;
+
+ //
+ // Find the token in the instance's map. EfiIp4Transmit put the
+ // token to the map. If that failed, NetMapFindKey will return NULL.
+ //
+ Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token);
+
+ if (Item != NULL) {
+ NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL);
+ }
+
+ if (Wrap->Sent) {
+ gBS->SignalEvent (Wrap->Token->Event);
+ }
+
+ NetFreePool (Wrap);
+}
+
+
+/**
+ The callback function to Ip4Output to update the transmit status.
+
+ @param Ip4Instance The Ip4Instance that request the transmit.
+ @param Packet The user's transmit request
+ @param IoStatus The result of the transmission
+ @param Flag Not used during transmission
+ @param Context The token's wrap.
+
+ @return None
+
+**/
+STATIC
+VOID
+Ip4OnPacketSent (
+ IP4_PROTOCOL *Ip4Instance,
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 Flag,
+ VOID *Context
+ )
+{
+ IP4_TXTOKEN_WRAP *Wrap;
+
+ //
+ // This is the transmission request from upper layer,
+ // not the IP4 driver itself.
+ //
+ ASSERT (Ip4Instance != NULL);
+
+ //
+ // The first fragment of the packet has been sent. Update
+ // the token's status. That is, if fragmented, the transmit's
+ // status is the first fragment's status. The Wrap will be
+ // release when all the fragments are release. Check the comments
+ // in Ip4FreeTxToken and Ip4Output for information.
+ //
+ Wrap = (IP4_TXTOKEN_WRAP *) Context;
+ Wrap->Token->Status = IoStatus;
+
+ NetbufFree (Wrap->Packet);
+}
+
+
+/**
+ Transmit the user's data asynchronously. When transmission
+ completed,the Token's status is updated and its event signalled.
+
+ @param This The IP4 child instance
+ @param Token The user's transmit token, which contains user's
+ data, the result and an event to signal when
+ completed.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_NOT_STARTED The IP4 child hasn't been started.
+ @retval EFI_SUCCESS The user's data has been successfully enqueued
+ for transmission.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiIp4Transmit (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_PROTOCOL *IpInstance;
+ IP4_INTERFACE *IpIf;
+ IP4_TXTOKEN_WRAP *Wrap;
+ EFI_IP4_TRANSMIT_DATA *TxData;
+ EFI_IP4_CONFIG_DATA *Config;
+ EFI_IP4_OVERRIDE_DATA *Override;
+ IP4_HEAD Head;
+ IP4_ADDR GateWay;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ BOOLEAN DontFragment;
+ UINT32 HeadLen;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ IpSb = IpInstance->Service;
+ IpIf = IpInstance->Interface;
+ Config = &IpInstance->ConfigData;
+
+ if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+
+ //
+ // make sure that token is properly formated
+ //
+ Status = Ip4TxTokenValid (Token, IpIf);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the token or signal already existed.
+ //
+ if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip4TokenExist, Token))) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Build the IP header, need to fill in the Tos, TotalLen, Id,
+ // fragment, Ttl, protocol, Src, and Dst.
+ //
+ TxData = Token->Packet.TxData;
+
+ NetCopyMem (&Head.Dst, &TxData->DestinationAddress, sizeof (IP4_ADDR));
+ Head.Dst = NTOHL (Head.Dst);
+
+ if (TxData->OverrideData) {
+ Override = TxData->OverrideData;
+ Head.Protocol = Override->Protocol;
+ Head.Tos = Override->TypeOfService;
+ Head.Ttl = Override->TimeToLive;
+ DontFragment = Override->DoNotFragment;
+
+ NetCopyMem (&Head.Src, &Override->SourceAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&GateWay, &Override->GatewayAddress, sizeof (IP4_ADDR));
+
+ Head.Src = NTOHL (Head.Src);
+ GateWay = NTOHL (GateWay);
+ } else {
+ Head.Src = IpIf->Ip;
+ GateWay = IP4_ALLZERO_ADDRESS;
+ Head.Protocol = Config->DefaultProtocol;
+ Head.Tos = Config->TypeOfService;
+ Head.Ttl = Config->TimeToLive;
+ DontFragment = Config->DoNotFragment;
+ }
+
+ Head.Fragment = IP4_HEAD_FRAGMENT_FIELD (DontFragment, FALSE, 0);
+ HeadLen = sizeof (IP4_HEAD) + ((TxData->OptionsLength + 3) &~0x03);
+
+ //
+ // If don't fragment and fragment needed, return error
+ //
+ if (DontFragment && (TxData->TotalDataLength + HeadLen > IpSb->SnpMode.MaxPacketSize)) {
+ Status = EFI_BAD_BUFFER_SIZE;
+ goto ON_EXIT;
+ }
+
+ //
+ // OK, it survives all the validation check. Wrap the token in
+ // a IP4_TXTOKEN_WRAP and the data in a netbuf
+ //
+ Status = EFI_OUT_OF_RESOURCES;
+ Wrap = NetAllocatePool (sizeof (IP4_TXTOKEN_WRAP));
+ if (Wrap == NULL) {
+ goto ON_EXIT;
+ }
+
+ Wrap->IpInstance = IpInstance;
+ Wrap->Token = Token;
+ Wrap->Sent = FALSE;
+ Wrap->Life = IP4_US_TO_SEC (Config->TransmitTimeout);
+ Wrap->Packet = NetbufFromExt (
+ (NET_FRAGMENT *) TxData->FragmentTable,
+ TxData->FragmentCount,
+ IP4_MAX_HEADLEN,
+ 0,
+ Ip4FreeTxToken,
+ Wrap
+ );
+
+ if (Wrap->Packet == NULL) {
+ NetFreePool (Wrap);
+ goto ON_EXIT;
+ }
+
+ Token->Status = EFI_NOT_READY;
+
+ if (EFI_ERROR (NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap))) {
+ //
+ // NetbufFree will call Ip4FreeTxToken, which in turn will
+ // free the IP4_TXTOKEN_WRAP. Now, the token wrap hasn't been
+ // enqueued.
+ //
+ NetbufFree (Wrap->Packet);
+ goto ON_EXIT;
+ }
+
+ Status = Ip4Output (
+ IpSb,
+ IpInstance,
+ Wrap->Packet,
+ &Head,
+ TxData->OptionsBuffer,
+ TxData->OptionsLength,
+ GateWay,
+ Ip4OnPacketSent,
+ Wrap
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Wrap->Packet);
+ goto ON_EXIT;
+ }
+
+ //
+ // Mark the packet sent, so when Ip4FreeTxToken is called, it
+ // will signal the upper layer.
+ //
+ Wrap->Sent = TRUE;
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Receive a packet for the upper layer. If there are packets
+ pending on the child's receive queue, the receive request
+ will be fulfilled immediately. Otherwise, the request is
+ enqueued. When receive request is completed, the status in
+ the Token is updated and its event is signalled.
+
+ @param This The IP4 child to receive packet.
+ @param Token The user's receive token
+
+ @retval EFI_INVALID_PARAMETER The token is invalid.
+ @retval EFI_NOT_STARTED The IP4 child hasn't been started
+ @retval EFI_ACCESS_DENIED The token or event is already queued.
+ @retval EFI_SUCCESS The receive request has been issued.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiIp4Receive (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_IP4_CONFIG_DATA *Config;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ Config = &IpInstance->ConfigData;
+
+ //
+ // Current Udp implementation creates an IP child for each Udp child.
+ // It initates a asynchronous receive immediately no matter whether
+ // there is no mapping or not. Disable this for now.
+ //
+#if 0
+ if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+#endif
+
+ //
+ // Check whether the toke is already on the receive queue.
+ //
+ Status = NetMapIterate (&IpInstance->RxTokens, Ip4TokenExist, Token);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Queue the token then check whether there is pending received packet.
+ //
+ Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Ip4InstanceDeliverPacket (IpInstance);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Cancel the transmitted but not recycled packet. If a matching
+ token is found, it will call Ip4CancelPacket to cancel the
+ packet. Ip4CancelPacket will cancel all the fragments of the
+ packet. When all the fragments are freed, the IP4_TXTOKEN_WRAP
+ will be deleted from the Map, and user's event signalled.
+ Because Ip4CancelPacket and other functions are all called in
+ line, so, after Ip4CancelPacket returns, the Item has been freed.
+
+ @param Map The IP4 child's transmit queue
+ @param Item The current transmitted packet to test.
+ @param Context The user's token to cancel.
+
+ @retval EFI_SUCCESS Continue to check the next Item.
+ @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.
+
+**/
+STATIC
+EFI_STATUS
+Ip4CancelTxTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ IP4_TXTOKEN_WRAP *Wrap;
+
+ Token = (EFI_IP4_COMPLETION_TOKEN *) Context;
+
+ //
+ // Return EFI_SUCCESS to check the next item in the map if
+ // this one doesn't match.
+ //
+ if ((Token != NULL) && (Token != Item->Key)) {
+ return EFI_SUCCESS;
+ }
+
+ Wrap = (IP4_TXTOKEN_WRAP *) Item->Value;
+ ASSERT (Wrap != NULL);
+
+ //
+ // Don't access the Item, Wrap and Token's members after this point.
+ // Item and wrap has been freed. And we no longer own the Token.
+ //
+ Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);
+
+ //
+ // If only one item is to be cancel, return EFI_ABORTED to stop
+ // iterating the map any more.
+ //
+ if (Token != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cancel the receive request. This is quiet simple, because
+ it is only enqueued in our local receive map.
+
+ @param Map The IP4 child's receive queue
+ @param Item Current receive request to cancel.
+ @param Context The user's token to cancel
+
+ @retval EFI_SUCCESS Continue to check the next receive request on the
+ queue.
+ @retval EFI_ABORTED The user's token (token != NULL) has been
+ cancelled.
+
+**/
+STATIC
+EFI_STATUS
+Ip4CancelRxTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ EFI_IP4_COMPLETION_TOKEN *This;
+
+ Token = (EFI_IP4_COMPLETION_TOKEN *) Context;
+ This = Item->Key;
+
+ if ((Token != NULL) && (Token != This)) {
+ return EFI_SUCCESS;
+ }
+
+ NetMapRemoveItem (Map, Item, NULL);
+
+ This->Status = EFI_ABORTED;
+ This->Packet.RxData = NULL;
+ gBS->SignalEvent (This->Event);
+
+ if (Token != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cancel the user's receive/transmit request.
+
+ @param IpInstance The IP4 child
+ @param Token The token to cancel. If NULL, all token will be
+ cancelled.
+
+ @retval EFI_SUCCESS The token is cancelled
+ @retval EFI_NOT_FOUND The token isn't found on either the
+ transmit/receive queue
+ @retval EFI_DEVICE_ERROR Not all token is cancelled when Token is NULL.
+
+**/
+EFI_STATUS
+Ip4Cancel (
+ IN IP4_PROTOCOL *IpInstance,
+ IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // First check the transmitted packet. Ip4CancelTxTokens returns
+ // EFI_ABORTED to mean that the token has been cancelled when
+ // token != NULL. So, return EFI_SUCCESS for this condition.
+ //
+ Status = NetMapIterate (&IpInstance->TxTokens, Ip4CancelTxTokens, Token);
+
+ if (EFI_ERROR (Status)) {
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+ }
+
+ //
+ // Check the receive queue. Ip4CancelRxTokens also returns EFI_ABORT
+ // for Token!=NULL and it is cancelled.
+ //
+ Status = NetMapIterate (&IpInstance->RxTokens, Ip4CancelRxTokens, Token);
+
+ if (EFI_ERROR (Status)) {
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+ }
+
+ //
+ // OK, if the Token is found when Token != NULL, the NetMapIterate
+ // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS.
+ //
+ if (Token != NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If Token == NULL, cancel all the tokens. return error if no
+ // all of them are cancelled.
+ //
+ if (!NetMapIsEmpty (&IpInstance->TxTokens) ||
+ !NetMapIsEmpty (&IpInstance->RxTokens)) {
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cancel the queued receive/transmit requests. If Token is NULL,
+ all the queued requests will be cancelled. It just validate
+ the parameter then pass them to Ip4Cancel.
+
+ @param This The IP4 child to cancel the request
+ @param Token The token to cancel, if NULL, cancel all.
+
+ @retval EFI_INVALID_PARAMETER This is NULL
+ @retval EFI_NOT_STARTED The IP4 child hasn't been configured.
+ @retval EFI_NO_MAPPING The IP4 child is configured to use the default,
+ but the default address hasn't been acquired.
+ @retval EFI_SUCCESS The Token is cancelled.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiIp4Cancel (
+ IN EFI_IP4_PROTOCOL *This,
+ IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto ON_EXIT;
+ }
+
+ Status = Ip4Cancel (IpInstance, Token);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Poll the network stack. The EFI network stack is poll based. There
+ is no interrupt support for the network interface card.
+
+ @param This The IP4 child to poll through
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid
+ @retval EFI_NOT_STARTED The IP4 child hasn't been configured.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiIp4Poll (
+ IN EFI_IP4_PROTOCOL *This
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);
+
+ if (IpInstance->State == IP4_STATE_UNCONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ Mnp = IpInstance->Service->Mnp;
+
+ //
+ // Don't lock the Poll function to enable the deliver of
+ // the packet polled up.
+ //
+ return Mnp->Poll (Mnp);
+}
+
+EFI_IP4_PROTOCOL
+mEfiIp4ProtocolTemplete = {
+ EfiIp4GetModeData,
+ EfiIp4Configure,
+ EfiIp4Groups,
+ EfiIp4Routes,
+ EfiIp4Transmit,
+ EfiIp4Receive,
+ EfiIp4Cancel,
+ EfiIp4Poll
+};
+
+
+/**
+ Decrease the life of the transmitted packets. If it is
+ decreased to zero, cancel the packet. This function is
+ called by Ip4packetTimerTicking which time out both the
+ received-but-not-delivered and transmitted-but-not-recycle
+ packets.
+
+ @param Map The IP4 child's transmit map.
+ @param Item Current transmitted packet
+ @param Context Not used.
+
+ @retval EFI_SUCCESS Always returns EFI_SUCCESS
+
+**/
+EFI_STATUS
+Ip4SentPacketTicking (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ IP4_TXTOKEN_WRAP *Wrap;
+
+ Wrap = (IP4_TXTOKEN_WRAP *) Item->Value;
+ ASSERT (Wrap != NULL);
+
+ if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {
+ Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The heart beat timer of IP4 service instance. It times out
+ all of its IP4 children's received-but-not-delivered and
+ transmitted-but-not-recycle packets, and provides time input
+ for its IGMP protocol.
+
+ @param Event The IP4 service instance's heart beat timer.
+ @param Context The IP4 service instance.
+
+ @return None
+
+**/
+VOID
+EFIAPI
+Ip4TimerTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP4_SERVICE *IpSb;
+
+ IpSb = (IP4_SERVICE *) Context;
+ NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE);
+
+ Ip4PacketTimerTicking (IpSb);
+ Ip4IgmpTicking (IpSb);
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h
new file mode 100644
index 0000000000..1bbee93237
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h
@@ -0,0 +1,258 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Ip4Impl.h
+
+Abstract:
+
+ Ip4 internal functions and type defintions.
+
+
+**/
+
+#ifndef __EFI_IP4_IMPL_H__
+#define __EFI_IP4_IMPL_H__
+
+#include <PiDxe.h>
+
+#include <Protocol/IP4.h>
+#include <Protocol/IP4Config.h>
+#include <Protocol/Arp.h>
+#include <Protocol/ManagedNetwork.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include "IP4Common.h"
+#include "IP4Driver.h"
+#include "IP4If.h"
+#include "Ip4Icmp.h"
+#include "IP4Option.h"
+#include "Ip4Igmp.h"
+#include "IP4Route.h"
+#include "IP4Input.h"
+#include "IP4Output.h"
+
+enum {
+ IP4_PROTOCOL_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', '4', 'P'),
+ IP4_SERVICE_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', '4', 'S'),
+
+ //
+ // The state of IP4 protocol. It starts from UNCONFIGED. if it is
+ // successfully configured, it goes to CONFIGED. if configure NULL
+ // is called, it becomes UNCONFIGED again. If (partly) destoried, it
+ // becomes DESTORY.
+ //
+ IP4_STATE_UNCONFIGED = 0,
+ IP4_STATE_CONFIGED,
+ IP4_STATE_DESTORY,
+
+ //
+ // The state of IP4 service. It starts from UNSTARTED. It transits
+ // to STARTED if autoconfigure is started. If default address is
+ // configured, it becomes CONFIGED. and if partly destoried, it goes
+ // to DESTORY.
+ //
+ IP4_SERVICE_UNSTARTED = 0,
+ IP4_SERVICE_STARTED,
+ IP4_SERVICE_CONFIGED,
+ IP4_SERVICE_DESTORY,
+};
+
+//
+// IP4_TXTOKEN_WRAP wraps the upper layer's transmit token.
+// The user's data is kept in the Packet. When fragment is
+// needed, each fragment of the Packet has a reference to the
+// Packet, no data is actually copied. The Packet will be
+// released when all the fragments of it have been recycled by
+// MNP. Upon then, the IP4_TXTOKEN_WRAP will be released, and
+// user's event signalled.
+//
+typedef struct {
+ IP4_PROTOCOL *IpInstance;
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ NET_BUF *Packet;
+ BOOLEAN Sent;
+ INTN Life;
+} IP4_TXTOKEN_WRAP;
+
+//
+// IP4_RXDATA_WRAP wraps the data IP4 child delivers to the
+// upper layers. The received packet is kept in the Packet.
+// The Packet itself may be constructured from some fragments.
+// All the fragments of the Packet is organized by a
+// IP4_ASSEMBLE_ENTRY structure. If the Packet is recycled by
+// the upper layer, the assemble entry and its associated
+// fragments will be freed at last.
+//
+typedef struct {
+ NET_LIST_ENTRY Link;
+ IP4_PROTOCOL *IpInstance;
+ NET_BUF *Packet;
+ EFI_IP4_RECEIVE_DATA RxData;
+} IP4_RXDATA_WRAP;
+
+typedef struct _IP4_PROTOCOL {
+ UINT32 Signature;
+
+ EFI_IP4_PROTOCOL Ip4Proto;
+ EFI_HANDLE Handle;
+ INTN State;
+
+ IP4_SERVICE *Service;
+ NET_LIST_ENTRY Link; // Link to all the IP protocol from the service
+
+ //
+ // User's transmit/receive tokens, and received/deliverd packets
+ //
+ NET_MAP RxTokens;
+ NET_MAP TxTokens; // map between (User's Token, IP4_TXTOKE_WRAP)
+ NET_LIST_ENTRY Received; // Received but not delivered packet
+ NET_LIST_ENTRY Delivered; // Delivered and to be recycled packets
+ EFI_LOCK RecycleLock;
+
+ //
+ // Instance's address and route tables. There are two route tables.
+ // RouteTable is used by the IP4 driver to route packet. EfiRouteTable
+ // is used to communicate the current route info to the upper layer.
+ //
+ IP4_INTERFACE *Interface;
+ NET_LIST_ENTRY AddrLink; // Ip instances with the same IP address.
+ IP4_ROUTE_TABLE *RouteTable;
+
+ EFI_IP4_ROUTE_TABLE *EfiRouteTable;
+ UINT32 EfiRouteCount;
+
+ //
+ // IGMP data for this instance
+ //
+ IP4_ADDR *Groups; // stored in network byte order
+ UINT32 GroupCount;
+
+ EFI_IP4_CONFIG_DATA ConfigData;
+
+} IP4_PROTOCOL;
+
+typedef struct _IP4_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ INTN State;
+ BOOLEAN InDestory;
+
+ //
+ // List of all the IP instances and interfaces, and default
+ // interface and route table and caches.
+ //
+ UINTN NumChildren;
+ NET_LIST_ENTRY Children;
+
+ NET_LIST_ENTRY Interfaces;
+
+ IP4_INTERFACE *DefaultInterface;
+ IP4_ROUTE_TABLE *DefaultRouteTable;
+
+ //
+ // Ip reassemble utilities, and IGMP data
+ //
+ IP4_ASSEMBLE_TABLE Assemble;
+ IGMP_SERVICE_DATA IgmpCtrl;
+
+ //
+ // Low level protocol used by this service instance
+ //
+ EFI_HANDLE Image;
+ EFI_HANDLE Controller;
+
+ EFI_HANDLE MnpChildHandle;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+
+ EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;
+ EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+ EFI_EVENT Timer;
+
+ //
+ // Auto configure staff
+ //
+ EFI_IP4_CONFIG_PROTOCOL *Ip4Config;
+ EFI_EVENT DoneEvent;
+ EFI_EVENT ReconfigEvent;
+
+ //
+ // The string representation of the current mac address of the
+ // NIC this IP4_SERVICE works on.
+ //
+ CHAR16 *MacString;
+} IP4_SERVICE;
+
+#define IP4_INSTANCE_FROM_PROTOCOL(Ip4) \
+ CR ((Ip4), IP4_PROTOCOL, Ip4Proto, IP4_PROTOCOL_SIGNATURE)
+
+#define IP4_SERVICE_FROM_PROTOCOL(Sb) \
+ CR ((Sb), IP4_SERVICE, ServiceBinding, IP4_SERVICE_SIGNATURE)
+
+#define IP4_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured)
+
+extern EFI_IP4_PROTOCOL mEfiIp4ProtocolTemplete;
+
+EFI_STATUS
+Ip4ServiceConfigMnp (
+ IN IP4_SERVICE *IpSb,
+ IN BOOLEAN Force
+ );
+
+VOID
+Ip4InitProtocol (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_PROTOCOL *IpInstance
+ );
+
+EFI_STATUS
+Ip4CleanProtocol (
+ IN IP4_PROTOCOL *IpInstance
+ );
+
+EFI_STATUS
+Ip4Cancel (
+ IN IP4_PROTOCOL *IpInstance,
+ IN EFI_IP4_COMPLETION_TOKEN *Token
+ );
+
+EFI_STATUS
+Ip4Groups (
+ IN IP4_PROTOCOL *IpInstance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *GroupAddress
+ );
+
+VOID
+EFIAPI
+Ip4TimerTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+EFI_STATUS
+Ip4SentPacketTicking (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ );
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c
new file mode 100644
index 0000000000..f5c4c9e1d4
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c
@@ -0,0 +1,1235 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Ip4Input.c
+
+Abstract:
+
+ IP4 input process.
+
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Create a empty assemble entry for the packet identified by
+ (Dst, Src, Id, Protocol). The default life for the packet is
+ 120 seconds.
+
+ @param Dst The destination address
+ @param Src The source address
+ @param Id The ID field in IP header
+ @param Protocol The protocol field in IP header
+
+ @return NULL if failed to allocate memory for the entry, otherwise
+ @return the point to just created reassemble entry.
+
+**/
+STATIC
+IP4_ASSEMBLE_ENTRY *
+Ip4CreateAssembleEntry (
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src,
+ IN UINT16 Id,
+ IN UINT8 Protocol
+ )
+{
+
+ IP4_ASSEMBLE_ENTRY *Assemble;
+
+ Assemble = NetAllocatePool (sizeof (IP4_ASSEMBLE_ENTRY));
+
+ if (Assemble == NULL) {
+ return NULL;
+ }
+
+ NetListInit (&Assemble->Link);
+ NetListInit (&Assemble->Fragments);
+
+ Assemble->Dst = Dst;
+ Assemble->Src = Src;
+ Assemble->Id = Id;
+ Assemble->Protocol = Protocol;
+ Assemble->TotalLen = 0;
+ Assemble->CurLen = 0;
+ Assemble->Head = NULL;
+ Assemble->Info = NULL;
+ Assemble->Life = IP4_FRAGMENT_LIFE;
+
+ return Assemble;
+}
+
+
+/**
+ Release all the fragments of a packet, then free the assemble entry
+
+ @param Assemble The assemble entry to free
+
+ @return None
+
+**/
+STATIC
+VOID
+Ip4FreeAssembleEntry (
+ IN IP4_ASSEMBLE_ENTRY *Assemble
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ NET_BUF *Fragment;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {
+ Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ NetListRemoveEntry (Entry);
+ NetbufFree (Fragment);
+ }
+
+ NetFreePool (Assemble);
+}
+
+
+/**
+ Initialize an already allocated assemble table. This is generally
+ the assemble table embedded in the IP4 service instance.
+
+ @param Table The assemble table to initialize.
+
+ @return NONE
+
+**/
+VOID
+Ip4InitAssembleTable (
+ IN IP4_ASSEMBLE_TABLE *Table
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
+ NetListInit (&Table->Bucket[Index]);
+ }
+}
+
+
+/**
+ Clean up the assemble table: remove all the fragments
+ and assemble entries.
+
+ @param Table The assemble table to clean up
+
+ @return None
+
+**/
+VOID
+Ip4CleanAssembleTable (
+ IN IP4_ASSEMBLE_TABLE *Table
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_ASSEMBLE_ENTRY *Assemble;
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {
+ Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);
+
+ NetListRemoveEntry (Entry);
+ Ip4FreeAssembleEntry (Assemble);
+ }
+ }
+}
+
+
+/**
+ Trim the packet to fit in [Start, End), and update the per
+ packet information.
+
+ @param Packet Packet to trim
+ @param Start The sequence of the first byte to fit in
+ @param End One beyond the sequence of last byte to fit in.
+
+ @return None
+
+**/
+STATIC
+VOID
+Ip4TrimPacket (
+ IN NET_BUF *Packet,
+ IN INTN Start,
+ IN INTN End
+ )
+{
+ IP4_CLIP_INFO *Info;
+ INTN Len;
+
+ Info = IP4_GET_CLIP_INFO (Packet);
+
+ ASSERT (Info->Start + Info->Length == Info->End);
+ ASSERT ((Info->Start < End) && (Start < Info->End));
+
+ if (Info->Start < Start) {
+ Len = Start - Info->Start;
+
+ NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);
+ Info->Start = Start;
+ Info->Length -= Len;
+ }
+
+ if (End < Info->End) {
+ Len = End - Info->End;
+
+ NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);
+ Info->End = End;
+ Info->Length -= Len;
+ }
+}
+
+
+/**
+ Release all the fragments of the packet. This is the callback for
+ the assembled packet's OnFree. It will free the assemble entry,
+ which in turn will free all the fragments of the packet.
+
+ @param Arg The assemble entry to free
+
+ @return None
+
+**/
+STATIC
+VOID
+Ip4OnFreeFragments (
+ IN VOID *Arg
+ )
+{
+ Ip4FreeAssembleEntry ((IP4_ASSEMBLE_ENTRY *) Arg);
+}
+
+
+/**
+ Reassemble the IP fragments. If all the fragments of the packet
+ have been received, it will wrap the packet in a net buffer then
+ return it to caller. If the packet can't be assembled, NULL is
+ return.
+
+ @param Table The assemble table used.
+ @param Packet The fragment to assemble
+
+ @return NULL if the packet can't be reassemble. The point to just assembled
+ @return packet if all the fragments of the packet have arrived.
+
+**/
+STATIC
+NET_BUF *
+Ip4Reassemble (
+ IN IP4_ASSEMBLE_TABLE *Table,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_HEAD *IpHead;
+ IP4_CLIP_INFO *This;
+ IP4_CLIP_INFO *Node;
+ IP4_ASSEMBLE_ENTRY *Assemble;
+ NET_LIST_ENTRY *Head;
+ NET_LIST_ENTRY *Prev;
+ NET_LIST_ENTRY *Cur;
+ NET_BUF *Fragment;
+ NET_BUF *NewPacket;
+ INTN Index;
+
+ IpHead = Packet->Ip;
+ This = IP4_GET_CLIP_INFO (Packet);
+
+ ASSERT (IpHead != NULL);
+
+ //
+ // First: find the related assemble entry
+ //
+ Assemble = NULL;
+ Index = IP4_ASSEMBLE_HASH (IpHead->Dst, IpHead->Src, IpHead->Id, IpHead->Protocol);
+
+ NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {
+ Assemble = NET_LIST_USER_STRUCT (Cur, IP4_ASSEMBLE_ENTRY, Link);
+
+ if ((Assemble->Dst == IpHead->Dst) && (Assemble->Src == IpHead->Src) &&
+ (Assemble->Id == IpHead->Id) && (Assemble->Protocol == IpHead->Protocol)) {
+ break;
+ }
+ }
+
+ //
+ // Create a new assemble entry if no assemble entry is related to this packet
+ //
+ if (Cur == &Table->Bucket[Index]) {
+ Assemble = Ip4CreateAssembleEntry (
+ IpHead->Dst,
+ IpHead->Src,
+ IpHead->Id,
+ IpHead->Protocol
+ );
+
+ if (Assemble == NULL) {
+ goto DROP;
+ }
+
+ NetListInsertHead (&Table->Bucket[Index], &Assemble->Link);
+ }
+
+ //
+ // Find the point to insert the packet: before the first
+ // fragment with THIS.Start < CUR.Start. the previous one
+ // has PREV.Start <= THIS.Start < CUR.Start.
+ //
+ Head = &Assemble->Fragments;
+
+ NET_LIST_FOR_EACH (Cur, Head) {
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (This->Start < IP4_GET_CLIP_INFO (Fragment)->Start) {
+ break;
+ }
+ }
+
+ //
+ // Check whether the current fragment overlaps with the previous one.
+ // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to
+ // check whether THIS.Start < PREV.End for overlap. If two fragments
+ // overlaps, trim the overlapped part off THIS fragment.
+ //
+ if ((Prev = Cur->ForwardLink) != Head) {
+ Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
+ Node = IP4_GET_CLIP_INFO (Fragment);
+
+ if (This->Start < Node->End) {
+ if (This->End <= Node->End) {
+ NetbufFree (Packet);
+ return NULL;
+ }
+
+ Ip4TrimPacket (Packet, Node->End, This->End);
+ }
+ }
+
+ //
+ // Insert the fragment into the packet. The fragment may be removed
+ // from the list by the following checks.
+ //
+ NetListInsertBefore (Cur, &Packet->List);
+
+ //
+ // Check the packets after the insert point. It holds that:
+ // THIS.Start <= NODE.Start < NODE.End. The equality holds
+ // if PREV and NEXT are continuous. THIS fragment may fill
+ // several holes. Remove the completely overlapped fragments
+ //
+ while (Cur != Head) {
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Node = IP4_GET_CLIP_INFO (Fragment);
+
+ //
+ // Remove fragments completely overlapped by this fragment
+ //
+ if (Node->End <= This->End) {
+ Cur = Cur->ForwardLink;
+
+ NetListRemoveEntry (&Fragment->List);
+ Assemble->CurLen -= Node->Length;
+
+ NetbufFree (Fragment);
+ continue;
+ }
+
+ //
+ // The conditions are: THIS.Start <= NODE.Start, and THIS.End <
+ // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.
+ // If two fragments start at the same offset, remove THIS fragment
+ // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).
+ //
+ if (Node->Start < This->End) {
+ if (This->Start == Node->Start) {
+ NetListRemoveEntry (&Packet->List);
+ goto DROP;
+ }
+
+ Ip4TrimPacket (Packet, This->Start, Node->Start);
+ }
+
+ break;
+ }
+
+ //
+ // Update the assemble info: increase the current length. If it is
+ // the frist fragment, update the packet's IP head and per packet
+ // info. If it is the last fragment, update the total length.
+ //
+ Assemble->CurLen += This->Length;
+
+ if (This->Start == 0) {
+ //
+ // Once the first fragment is enqueued, it can't be removed
+ // from the fragment list. So, Assemble->Head always point
+ // to valid memory area.
+ //
+ ASSERT (Assemble->Head == NULL);
+
+ Assemble->Head = IpHead;
+ Assemble->Info = IP4_GET_CLIP_INFO (Packet);
+ }
+
+ //
+ // Don't update the length more than once.
+ //
+ if (IP4_LAST_FRAGMENT (IpHead->Fragment) && (Assemble->TotalLen == 0)) {
+ Assemble->TotalLen = This->End;
+ }
+
+ //
+ // Deliver the whole packet if all the fragments received.
+ // All fragments received if:
+ // 1. received the last one, so, the totoal length is know
+ // 2. received all the data. If the last fragment on the
+ // queue ends at the total length, all data is received.
+ //
+ if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {
+
+ NetListRemoveEntry (&Assemble->Link);
+
+ //
+ // If the packet is properly formated, the last fragment's End
+ // equals to the packet's total length. Otherwise, the packet
+ // is a fake, drop it now.
+ //
+ Fragment = NET_LIST_USER_STRUCT (Head->BackLink, NET_BUF, List);
+
+ if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) {
+ Ip4FreeAssembleEntry (Assemble);
+ return NULL;
+ }
+
+ //
+ // Wrap the packet in a net buffer then deliver it up
+ //
+ NewPacket = NetbufFromBufList (
+ &Assemble->Fragments,
+ 0,
+ 0,
+ Ip4OnFreeFragments,
+ Assemble
+ );
+
+ if (NewPacket == NULL) {
+ Ip4FreeAssembleEntry (Assemble);
+ return NULL;
+ }
+
+ NewPacket->Ip = Assemble->Head;
+ CopyMem (IP4_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP4_CLIP_INFO));
+ return NewPacket;
+ }
+
+ return NULL;
+
+DROP:
+ NetbufFree (Packet);
+ return NULL;
+}
+
+
+/**
+ The IP4 input routine. It is called by the IP4_INTERFACE when a
+ IP4 fragment is received from MNP.
+
+ @param Ip4Instance The IP4 child that request the receive, most like
+ it is NULL.
+ @param Packet The IP4 packet received.
+ @param IoStatus The return status of receive request.
+ @param Flag The link layer flag for the packet received, such
+ as multicast.
+ @param Context The IP4 service instance that own the MNP.
+
+ @return None
+
+**/
+VOID
+Ip4AccpetFrame (
+ IN IP4_PROTOCOL *Ip4Instance,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus,
+ IN UINT32 Flag,
+ IN VOID *Context
+ )
+{
+ IP4_SERVICE *IpSb;
+ IP4_CLIP_INFO *Info;
+ IP4_HEAD *Head;
+ UINT32 HeadLen;
+ UINT32 OptionLen;
+ UINT32 TotalLen;
+ UINT16 Checksum;
+
+ IpSb = (IP4_SERVICE *) Context;
+
+ if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTORY)) {
+ goto DROP;
+ }
+
+ //
+ // Check that the IP4 header is correctly formated
+ //
+ if (Packet->TotalSize < IP4_MIN_HEADLEN) {
+ goto RESTART;
+ }
+
+ Head = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);
+ HeadLen = (Head->HeadLen << 2);
+ TotalLen = NTOHS (Head->TotalLen);
+
+ //
+ // Mnp may deliver frame trailer sequence up, trim it off.
+ //
+ if (TotalLen < Packet->TotalSize) {
+ NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE);
+ }
+
+ if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) ||
+ (TotalLen < HeadLen) || (TotalLen != Packet->TotalSize)) {
+ goto RESTART;
+ }
+
+ //
+ // Some OS may send IP packets without checksum.
+ //
+ Checksum = ~NetblockChecksum ((UINT8 *) Head, HeadLen);
+
+ if ((Head->Checksum != 0) && (Checksum != 0)) {
+ goto RESTART;
+ }
+
+ //
+ // Convert the IP header to host byte order, then get the per packet info.
+ //
+ Packet->Ip = Ip4NtohHead (Head);
+
+ Info = IP4_GET_CLIP_INFO (Packet);
+ Info->LinkFlag = Flag;
+ Info->CastType = Ip4GetHostCast (IpSb, Head->Dst, Head->Src);
+ Info->Start = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3;
+ Info->Length = Head->TotalLen - HeadLen;
+ Info->End = Info->Start + Info->Length;
+ Info->Status = EFI_SUCCESS;
+
+ //
+ // The packet is destinated to us if the CastType is non-zero.
+ //
+ if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the options. Don't call the Ip4OptionIsValid if
+ // there is no option to save some CPU process.
+ //
+ OptionLen = HeadLen - IP4_MIN_HEADLEN;
+
+ if ((OptionLen > 0) && !Ip4OptionIsValid ((UINT8 *) (Head + 1), OptionLen, TRUE)) {
+ goto RESTART;
+ }
+
+ //
+ // Trim the head off, after this point, the packet is headless.
+ // and Packet->TotalLen == Info->Length.
+ //
+ NetbufTrim (Packet, HeadLen, TRUE);
+
+ //
+ // Reassemble the packet if this is a fragment. The packet is a
+ // fragment if its head has MF (more fragment) set, or it starts
+ // at non-zero byte.
+ //
+ if ((Head->Fragment & IP4_HEAD_MF_MASK) || (Info->Start != 0)) {
+ //
+ // Drop the fragment if DF is set but it is fragmented. Gateway
+ // need to send a type 4 destination unreache ICMP message here.
+ //
+ if (Head->Fragment & IP4_HEAD_DF_MASK) {
+ goto RESTART;
+ }
+
+ //
+ // The length of all but the last fragments is in the unit of 8 bytes.
+ //
+ if ((Head->Fragment & IP4_HEAD_MF_MASK) && (Info->Length % 8 != 0)) {
+ goto RESTART;
+ }
+
+ Packet = Ip4Reassemble (&IpSb->Assemble, Packet);
+
+ //
+ // Packet assembly isn't complete, start receive more packet.
+ //
+ if (Packet == NULL) {
+ goto RESTART;
+ }
+ }
+
+ //
+ // Packet may have been changed. Head, HeadLen, TotalLen, and
+ // info must be reloaded bofore use. The ownership of the packet
+ // is transfered to the packet process logic.
+ //
+ Head = Packet->Ip;
+ IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;
+
+ switch (Head->Protocol) {
+ case IP4_PROTO_ICMP:
+ Ip4IcmpHandle (IpSb, Head, Packet);
+ break;
+
+ case IP4_PROTO_IGMP:
+ Ip4IgmpHandle (IpSb, Head, Packet);
+ break;
+
+ default:
+ Ip4Demultiplex (IpSb, Head, Packet);
+ }
+
+ Packet = NULL;
+
+RESTART:
+ Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);
+
+DROP:
+ if (Packet != NULL) {
+ NetbufFree (Packet);
+ }
+
+ return ;
+}
+
+
+/**
+ Check whether this IP child accepts the packet.
+
+ @param IpInstance The IP child to check
+ @param Head The IP header of the packet
+ @param Packet The data of the packet
+
+ @return TRUE if the child wants to receive the packet, otherwise return FALSE.
+
+**/
+BOOLEAN
+Ip4InstanceFrameAcceptable (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_ERROR_HEAD Icmp;
+ EFI_IP4_CONFIG_DATA *Config;
+ IP4_CLIP_INFO *Info;
+ UINT16 Proto;
+ UINT32 Index;
+
+ Config = &IpInstance->ConfigData;
+
+ //
+ // Dirty trick for the Tiano UEFI network stack implmentation. If
+ // ReceiveTimeout == -1, the receive of the packet for this instance
+ // is disabled. The UEFI spec don't have such captibility. We add
+ // this to improve the performance because IP will make a copy of
+ // the received packet for each accepting instance. Some IP instances
+ // used by UDP/TCP only send packets, they don't wants to receive.
+ //
+ if (Config->ReceiveTimeout == (UINT32)(-1)) {
+ return FALSE;
+ }
+
+ if (Config->AcceptPromiscuous) {
+ return TRUE;
+ }
+
+ //
+ // Use protocol from the IP header embedded in the ICMP error
+ // message to filter, instead of ICMP itself. ICMP handle will
+ // can Ip4Demultiplex to deliver ICMP errors.
+ //
+ Proto = Head->Protocol;
+
+ if (Proto == IP4_PROTO_ICMP) {
+ NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *) &Icmp.Head);
+
+ if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
+ if (!Config->AcceptIcmpErrors) {
+ return FALSE;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+ Proto = Icmp.IpHead.Protocol;
+ }
+ }
+
+ //
+ // Match the protocol
+ //
+ if (!Config->AcceptAnyProtocol && (Proto != Config->DefaultProtocol)) {
+ return FALSE;
+ }
+
+ //
+ // Check for broadcast, the caller has computed the packet's
+ // cast type for this child's interface.
+ //
+ Info = IP4_GET_CLIP_INFO (Packet);
+
+ if (IP4_IS_BROADCAST (Info->CastType)) {
+ return Config->AcceptBroadcast;
+ }
+
+ //
+ // If it is a multicast packet, check whether we are in the group.
+ //
+ if (Info->CastType == IP4_MULTICAST) {
+ //
+ // Receive the multicast if the instance wants to receive all packets.
+ //
+ if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) {
+ return TRUE;
+ }
+
+ for (Index = 0; Index < IpInstance->GroupCount; Index++) {
+ if (IpInstance->Groups[Index] == HTONL (Head->Dst)) {
+ break;
+ }
+ }
+
+ return (BOOLEAN)(Index < IpInstance->GroupCount);
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Enqueue a shared copy of the packet to the IP4 child if the
+ packet is acceptable to it. Here the data of the packet is
+ shared, but the net buffer isn't.
+
+ @param IpInstance The IP4 child to enqueue the packet to
+ @param Head The IP header of the received packet
+ @param Packet The data of the received packet
+
+ @retval EFI_NOT_STARTED The IP child hasn't been configured.
+ @retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource
+ @retval EFI_SUCCESS A shared copy the packet is enqueued to the child.
+
+**/
+EFI_STATUS
+Ip4InstanceEnquePacket (
+ IN IP4_PROTOCOL *IpInstance,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_CLIP_INFO *Info;
+ NET_BUF *Clone;
+
+ //
+ // Check whether the packet is acceptable to this instance.
+ //
+ if (IpInstance->State != IP4_STATE_CONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!Ip4InstanceFrameAcceptable (IpInstance, Head, Packet)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Enque a shared copy of the packet.
+ //
+ Clone = NetbufClone (Packet);
+
+ if (Clone == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Set the receive time out for the assembled packet. If it expires,
+ // packet will be removed from the queue.
+ //
+ Info = IP4_GET_CLIP_INFO (Clone);
+ Info->Life = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);
+
+ NetListInsertTail (&IpInstance->Received, &Clone->List);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The signal handle of IP4's recycle event. It is called back
+ when the upper layer release the packet.
+
+ @param Event The IP4's recycle event.
+ @param Context The context of the handle, which is a
+ IP4_RXDATA_WRAP
+
+ @return None
+
+**/
+STATIC
+VOID
+EFIAPI
+Ip4OnRecyclePacket (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP4_RXDATA_WRAP *Wrap;
+
+ Wrap = (IP4_RXDATA_WRAP *) Context;
+
+ NET_TRYLOCK (&Wrap->IpInstance->RecycleLock);
+ NetListRemoveEntry (&Wrap->Link);
+ NET_UNLOCK (&Wrap->IpInstance->RecycleLock);
+
+ ASSERT (!NET_BUF_SHARED (Wrap->Packet));
+ NetbufFree (Wrap->Packet);
+
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);
+ NetFreePool (Wrap);
+}
+
+
+/**
+ Wrap the received packet to a IP4_RXDATA_WRAP, which will be
+ delivered to the upper layer. Each IP4 child that accepts the
+ packet will get a not-shared copy of the packet which is wrapped
+ in the IP4_RXDATA_WRAP. The IP4_RXDATA_WRAP->RxData is passed
+ to the upper layer. Upper layer will signal the recycle event in
+ it when it is done with the packet.
+
+ @param IpInstance The IP4 child to receive the packet
+ @param Packet The packet to deliver up.
+
+ @return NULL if failed to wrap the packet, otherwise the wrapper.
+
+**/
+IP4_RXDATA_WRAP *
+Ip4WrapRxData (
+ IN IP4_PROTOCOL *IpInstance,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_RXDATA_WRAP *Wrap;
+ EFI_IP4_RECEIVE_DATA *RxData;
+ EFI_STATUS Status;
+
+ Wrap = NetAllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum));
+
+ if (Wrap == NULL) {
+ return NULL;
+ }
+
+ NetListInit (&Wrap->Link);
+
+ Wrap->IpInstance = IpInstance;
+ Wrap->Packet = Packet;
+ RxData = &Wrap->RxData;
+
+ NetZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ NET_TPL_RECYCLE,
+ Ip4OnRecyclePacket,
+ Wrap,
+ &RxData->RecycleSignal
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetFreePool (Wrap);
+ return NULL;
+ }
+
+ ASSERT (Packet->Ip != NULL);
+
+ //
+ // The application expects a network byte order header.
+ //
+ RxData->HeaderLength = (Packet->Ip->HeadLen << 2);
+ RxData->Header = (EFI_IP4_HEADER *) Ip4NtohHead (Packet->Ip);
+
+ RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN;
+ RxData->Options = NULL;
+
+ if (RxData->OptionsLength != 0) {
+ RxData->Options = (VOID *) (RxData->Header + 1);
+ }
+
+ RxData->DataLength = Packet->TotalSize;
+
+ //
+ // Build the fragment table to be delivered up.
+ //
+ RxData->FragmentCount = Packet->BlockOpNum;
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);
+
+ return Wrap;
+}
+
+
+/**
+ Deliver the received packets to upper layer if there are both received
+ requests and enqueued packets. If the enqueued packet is shared, it will
+ duplicate it to a non-shared packet, release the shared packet, then
+ deliver the non-shared packet up.
+
+ @param IpInstance The IP child to deliver the packet up.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the
+ packets.
+ @retval EFI_SUCCESS All the enqueued packets that can be delivered
+ are delivered up.
+
+**/
+EFI_STATUS
+Ip4InstanceDeliverPacket (
+ IN IP4_PROTOCOL *IpInstance
+ )
+{
+ EFI_IP4_COMPLETION_TOKEN *Token;
+ IP4_RXDATA_WRAP *Wrap;
+ NET_BUF *Packet;
+ NET_BUF *Dup;
+ UINT8 *Head;
+
+ //
+ // Deliver a packet if there are both a packet and a receive token.
+ //
+ while (!NetListIsEmpty (&IpInstance->Received) &&
+ !NetMapIsEmpty (&IpInstance->RxTokens)) {
+
+ Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);
+
+ if (!NET_BUF_SHARED (Packet)) {
+ //
+ // If this is the only instance that wants the packet, wrap it up.
+ //
+ Wrap = Ip4WrapRxData (IpInstance, Packet);
+
+ if (Wrap == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetListRemoveEntry (&Packet->List);
+
+ } else {
+ //
+ // Create a duplicated packet if this packet is shared
+ //
+ Dup = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);
+
+ if (Dup == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy the IP head over. The packet to deliver up is
+ // headless. Trim the head off after copy. The IP head
+ // may be not continuous before the data.
+ //
+ Head = NetbufAllocSpace (Dup, IP4_MAX_HEADLEN, NET_BUF_HEAD);
+ Dup->Ip = (IP4_HEAD *) Head;
+
+ NetCopyMem (Head, Packet->Ip, Packet->Ip->HeadLen << 2);
+ NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE);
+
+ Wrap = Ip4WrapRxData (IpInstance, Dup);
+
+ if (Wrap == NULL) {
+ NetbufFree (Dup);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetListRemoveEntry (&Packet->List);
+ NetbufFree (Packet);
+
+ Packet = Dup;
+ }
+
+ //
+ // Insert it into the delivered packet, then get a user's
+ // receive token, pass the wrapped packet up.
+ //
+ NET_TRYLOCK (&IpInstance->RecycleLock);
+ NetListInsertHead (&IpInstance->Delivered, &Wrap->Link);
+ NET_UNLOCK (&IpInstance->RecycleLock);
+
+ Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL);
+ Token->Status = IP4_GET_CLIP_INFO (Packet)->Status;
+ Token->Packet.RxData = &Wrap->RxData;
+
+ gBS->SignalEvent (Token->Event);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Enqueue a received packet to all the IP children that share
+ the same interface.
+
+ @param IpSb The IP4 service instance that receive the packet
+ @param Head The header of the received packet
+ @param Packet The data of the received packet
+ @param IpIf The interface to enqueue the packet to
+
+ @return The number of the IP4 children that accepts the packet
+
+**/
+INTN
+Ip4InterfaceEnquePacket (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN IP4_INTERFACE *IpIf
+ )
+{
+ IP4_PROTOCOL *IpInstance;
+ IP4_CLIP_INFO *Info;
+ NET_LIST_ENTRY *Entry;
+ INTN Enqueued;
+ INTN LocalType;
+ INTN SavedType;
+
+ //
+ // First, check that the packet is acceptable to this interface
+ // and find the local cast type for the interface. A packet sent
+ // to say 192.168.1.1 should NOT be delliever to 10.0.0.1 unless
+ // promiscuous receiving.
+ //
+ LocalType = 0;
+ Info = IP4_GET_CLIP_INFO (Packet);
+
+ if ((Info->CastType == IP4_MULTICAST) || (Info->CastType == IP4_LOCAL_BROADCAST)) {
+ //
+ // If the CastType is multicast, don't need to filter against
+ // the group address here, Ip4InstanceFrameAcceptable will do
+ // that later.
+ //
+ LocalType = Info->CastType;
+
+ } else {
+ //
+ // Check the destination againist local IP. If the station
+ // address is 0.0.0.0, it means receiving all the IP destined
+ // to local non-zero IP. Otherwise, it is necessary to compare
+ // the destination to the interface's IP address.
+ //
+ if (IpIf->Ip == IP4_ALLZERO_ADDRESS) {
+ LocalType = IP4_LOCAL_HOST;
+
+ } else {
+ LocalType = Ip4GetNetCast (Head->Dst, IpIf);
+
+ if ((LocalType == 0) && IpIf->PromiscRecv) {
+ LocalType = IP4_PROMISCUOUS;
+ }
+ }
+ }
+
+ if (LocalType == 0) {
+ return 0;
+ }
+
+ //
+ // Iterate through the ip instances on the interface, enqueue
+ // the packet if filter passed. Save the original cast type,
+ // and pass the local cast type to the IP children on the
+ // interface. The global cast type will be restored later.
+ //
+ SavedType = Info->CastType;
+ Info->CastType = LocalType;
+
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
+ NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE);
+
+ if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {
+ Enqueued++;
+ }
+ }
+
+ Info->CastType = SavedType;
+ return Enqueued;
+}
+
+
+/**
+ Deliver the packet for each IP4 child on the interface.
+
+ @param IpSb The IP4 service instance that received the packet
+ @param IpIf The IP4 interface to deliver the packet.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS now
+
+**/
+EFI_STATUS
+Ip4InterfaceDeliverPacket (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_INTERFACE *IpIf
+ )
+{
+ IP4_PROTOCOL *Ip4Instance;
+ NET_LIST_ENTRY *Entry;
+
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
+ Ip4InstanceDeliverPacket (Ip4Instance);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Demultiple the packet. the packet delivery is processed in two
+ passes. The first pass will enque a shared copy of the packet
+ to each IP4 child that accepts the packet. The second pass will
+ deliver a non-shared copy of the packet to each IP4 child that
+ has pending receive requests. Data is copied if more than one
+ child wants to consume the packet bacause each IP child need
+ its own copy of the packet to make changes.
+
+ @param IpSb The IP4 service instance that received the packet
+ @param Head The header of the received packet
+ @param Packet The data of the received packet
+
+ @retval EFI_NOT_FOUND No IP child accepts the packet
+ @retval EFI_SUCCESS The packet is enqueued or delivered to some IP
+ children.
+
+**/
+EFI_STATUS
+Ip4Demultiplex (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IP4_INTERFACE *IpIf;
+ INTN Enqueued;
+
+ //
+ // Two pass delivery: first, enque a shared copy of the packet
+ // to each instance that accept the packet.
+ //
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured) {
+ Enqueued += Ip4InterfaceEnquePacket (IpSb, Head, Packet, IpIf);
+ }
+ }
+
+ //
+ // Second: deliver a duplicate of the packet to each instance.
+ // Release the local reference first, so that the last instance
+ // getting the packet will not copy the data.
+ //
+ NetbufFree (Packet);
+
+ if (Enqueued == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured) {
+ Ip4InterfaceDeliverPacket (IpSb, IpIf);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Timeout the fragment and enqueued packets.
+
+ @param IpSb The IP4 service instance to timeout
+
+ @return None
+
+**/
+VOID
+Ip4PacketTimerTicking (
+ IN IP4_SERVICE *IpSb
+ )
+{
+ NET_LIST_ENTRY *InstanceEntry;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_PROTOCOL *IpInstance;
+ IP4_ASSEMBLE_ENTRY *Assemble;
+ NET_BUF *Packet;
+ IP4_CLIP_INFO *Info;
+ UINT32 Index;
+
+ //
+ // First, time out the fragments. The packet's life is counting down
+ // once the first-arrived fragment was received.
+ //
+ for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->Assemble.Bucket[Index]) {
+ Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);
+
+ if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {
+ NetListRemoveEntry (Entry);
+ Ip4FreeAssembleEntry (Assemble);
+ }
+ }
+ }
+
+ NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP4_PROTOCOL, Link);
+
+ //
+ // Second, time out the assembled packets enqueued on each IP child.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {
+ Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Info = IP4_GET_CLIP_INFO (Packet);
+
+ if ((Info->Life > 0) && (--Info->Life == 0)) {
+ NetListRemoveEntry (Entry);
+ NetbufFree (Packet);
+ }
+ }
+
+ //
+ // Third: time out the transmitted packets.
+ //
+ NetMapIterate (&IpInstance->TxTokens, Ip4SentPacketTicking, NULL);
+ }
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h
new file mode 100644
index 0000000000..9a3af8fba7
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h
@@ -0,0 +1,138 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Ip4Input.h
+
+Abstract:
+
+
+**/
+
+#ifndef __EFI_IP4_INPUT_H__
+#define __EFI_IP4_INPUT_H__
+
+enum {
+ IP4_MIN_HEADLEN = 20,
+ IP4_MAX_HEADLEN = 60,
+
+ IP4_ASSEMLE_HASH_SIZE = 31,
+ IP4_FRAGMENT_LIFE = 120,
+ IP4_MAX_PACKET_SIZE = 65535,
+};
+
+//
+// Per packet information for input process. LinkFlag specifies whether
+// the packet is received as Link layer unicast, multicast or broadcast.
+// The CastType is the IP layer cast type, such as IP multicast or unicast.
+// Start, End and Length are staffs used to assemble the packets. Start
+// is the sequence number of the first byte of data in the packet. Length
+// is the number of bytes of data. End = Start + Length, that is, the
+// sequence number of last byte + 1. Each assembled packet has a count down
+// life. If it isn't consumed before Life reaches zero, the packet is released.
+//
+typedef struct {
+ UINTN LinkFlag;
+ INTN CastType;
+ INTN Start;
+ INTN End;
+ INTN Length;
+ UINT32 Life;
+ EFI_STATUS Status;
+} IP4_CLIP_INFO;
+
+//
+// Structure used to assemble IP packets.
+//
+typedef struct {
+ NET_LIST_ENTRY Link;
+
+ //
+ // Identity of one IP4 packet. Each fragment of a packet has
+ // the same (Dst, Src, Id, Protocol).
+ //
+ IP4_ADDR Dst;
+ IP4_ADDR Src;
+ UINT16 Id;
+ UINT8 Protocol;
+
+ INTN TotalLen;
+ INTN CurLen;
+ NET_LIST_ENTRY Fragments; // List of all the fragments of this packet
+
+ IP4_HEAD *Head; // IP head of the first fragment
+ IP4_CLIP_INFO *Info; // Per packet info of the first fragment
+ INTN Life; // Count down life for the packet.
+} IP4_ASSEMBLE_ENTRY;
+
+//
+// Each Ip service instance has an assemble table to reassemble
+// the packets before delivery to its children. It is organized
+// as hash table.
+//
+typedef struct {
+ NET_LIST_ENTRY Bucket[IP4_ASSEMLE_HASH_SIZE];
+} IP4_ASSEMBLE_TABLE;
+
+#define IP4_GET_CLIP_INFO(Packet) ((IP4_CLIP_INFO *) ((Packet)->ProtoData))
+
+#define IP4_ASSEMBLE_HASH(Dst, Src, Id, Proto) \
+ (((Dst) + (Src) + ((Id) << 16) + (Proto)) % IP4_ASSEMLE_HASH_SIZE)
+
+#define IP4_RXDATA_WRAP_SIZE(NumFrag) \
+ (sizeof (IP4_RXDATA_WRAP) + sizeof (EFI_IP4_FRAGMENT_DATA) * ((NumFrag) - 1))
+
+VOID
+Ip4InitAssembleTable (
+ IN IP4_ASSEMBLE_TABLE *Table
+ );
+
+VOID
+Ip4CleanAssembleTable (
+ IN IP4_ASSEMBLE_TABLE *Table
+ );
+
+VOID
+Ip4AccpetFrame (
+ IN IP4_PROTOCOL *Ip4Instance,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus,
+ IN UINT32 Flag,
+ IN VOID *Context
+ );
+
+EFI_STATUS
+Ip4Demultiplex (
+ IN IP4_SERVICE *SbInstance,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ );
+
+INTN
+Ip4InterfaceEnquePacket (
+ IN IP4_SERVICE *SbInstance,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN IP4_INTERFACE *Interface
+ );
+
+EFI_STATUS
+Ip4InstanceDeliverPacket (
+ IN IP4_PROTOCOL *Ip4Instance
+ );
+
+VOID
+Ip4PacketTimerTicking (
+ IN IP4_SERVICE *IpSb
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c
new file mode 100644
index 0000000000..e1f059e89f
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c
@@ -0,0 +1,231 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Ip4Option.c
+
+Abstract:
+
+ IP4 option support functions
+
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Validate the IP4 option format for both the packets we received
+ and will transmit. It will compute the ICMP error message fields
+ if the option is mal-formated. But this information isn't used.
+
+ @param Option The first byte of the option
+ @param OptionLen The length of the whole option
+ @param Rcvd The option is from the packet we received if TRUE,
+ otherwise the option we wants to transmit.
+
+ @return TRUE: The option is properly formated
+ @return FALSE: The option is mal-formated
+
+**/
+BOOLEAN
+Ip4OptionIsValid (
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN BOOLEAN Rcvd
+ )
+{
+ UINT32 Cur;
+ UINT32 Len;
+ UINT32 Point;
+ UINT8 IcmpType;
+ UINT8 IcmpCode;
+ UINT32 IcmpPoint;
+
+ IcmpType = ICMP_PARAMETER_PROBLEM;
+ IcmpCode = 0;
+ IcmpPoint = 0;
+
+ Cur = 0;
+
+ while (Cur < OptionLen) {
+ switch (Option[Cur]) {
+ case IP4_OPTION_NOP:
+ Cur++;
+ break;
+
+ case IP4_OPTION_EOP:
+ Cur = OptionLen;
+ break;
+
+ case IP4_OPTION_LSRR:
+ case IP4_OPTION_SSRR:
+ case IP4_OPTION_RR:
+ Len = Option[Cur + 1];
+ Point = Option[Cur + 2];
+
+ //
+ // SRR/RR options are formated as |Type|Len|Point|Ip1|Ip2|...
+ //
+ if ((OptionLen - Cur < Len) || (Len < 3) || ((Len - 3) % 4 != 0)) {
+ IcmpPoint = Cur + 1;
+ return FALSE;
+ }
+
+ if ((Point > Len + 1) || (Point % 4 != 0)) {
+ IcmpPoint = Cur + 2;
+ return FALSE;
+ }
+
+ //
+ // The Point must point pass the last entry if the packet is received
+ // by us. It must point to 4 if the packet is to be sent by us for
+ // source route option.
+ //
+ if ((Option[Cur] != IP4_OPTION_RR) &&
+ ((Rcvd && (Point != Len + 1)) || (!Rcvd && (Point != 4)))) {
+
+ IcmpType = ICMP_DEST_UNREACHABLE;
+ IcmpCode = ICMP_SOURCEROUTE_FAILED;
+ return FALSE;
+ }
+
+ Cur += Len;
+ break;
+
+ default:
+ Len = Option[Cur + 1];
+
+ if ((OptionLen - Cur < Len) || (Len < 2)) {
+ IcmpPoint = Cur + 1;
+ return FALSE;
+ }
+
+ Cur = Cur + Len;
+ break;
+ }
+
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Copy the option from the original option to buffer. It
+ handles the details such as:
+ 1. whether copy the single IP4 option to the first/non-first
+ fragments.
+ 2. Pad the options copied over to aligened to 4 bytes.
+
+ @param Option The original option to copy from
+ @param OptionLen The length of the original option
+ @param FirstFragment Whether it is the first fragment
+ @param Buf The buffer to copy options to
+ @param BufLen The length of the buffer
+
+ @retval EFI_SUCCESS The options are copied over
+ @retval EFI_BUFFER_TOO_SMALL The buffer caller provided is too small.
+
+**/
+EFI_STATUS
+Ip4CopyOption (
+ IN UINT8 *Option,
+ IN UINT32 OptionLen,
+ IN BOOLEAN FirstFragment,
+ IN UINT8 *Buf, OPTIONAL
+ IN OUT UINT32 *BufLen
+ )
+{
+ UINT8 OptBuf[40];
+ UINT32 Cur;
+ UINT32 Next;
+ UINT8 Type;
+ UINT32 Len;
+
+ ASSERT ((BufLen != NULL) && (OptionLen <= 40));
+
+ Cur = 0;
+ Next = 0;
+
+ while (Cur < OptionLen) {
+ Type = Option[Cur];
+ Len = Option[Cur + 1];
+
+ if (Type == IP4_OPTION_NOP) {
+ //
+ // Keep the padding, in case that the sender wants to align
+ // the option, say, to 4 bytes
+ //
+ OptBuf[Next] = IP4_OPTION_NOP;
+ Next++;
+ Cur++;
+
+ } else if (Type == IP4_OPTION_EOP) {
+ //
+ // Don't append the EOP to avoid including only a EOP option
+ //
+ break;
+
+ } else {
+ //
+ // don't copy options that is only valid for the first fragment
+ //
+ if (FirstFragment || (Type & IP4_OPTION_COPY_MASK)) {
+ NetCopyMem (OptBuf + Next, Option + Cur, Len);
+ Next += Len;
+ }
+
+ Cur += Len;
+ }
+ }
+
+ //
+ // Don't append an EOP only option.
+ //
+ if (Next == 0) {
+ *BufLen = 0;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Append an EOP if the end of option doesn't coincide with the
+ // end of the IP header, that is, isn't aligned to 4 bytes..
+ //
+ if ((Next % 4) != 0) {
+ OptBuf[Next] = IP4_OPTION_EOP;
+ Next++;
+ }
+
+ //
+ // Head length is in the unit of 4 bytes. Now, Len is the
+ // acutal option length to appear in the IP header.
+ //
+ Len = ((Next + 3) &~0x03);
+
+ //
+ // If the buffer is too small, set the BufLen then return
+ //
+ if ((Buf == NULL) || (*BufLen < Len)) {
+ *BufLen = Len;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Copy the option to the Buf, zero the buffer first to pad
+ // the options with NOP to align to 4 bytes.
+ //
+ NetZeroMem (Buf, Len);
+ NetCopyMem (Buf, OptBuf, Next);
+ *BufLen = Len;
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h
new file mode 100644
index 0000000000..6af885f25b
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h
@@ -0,0 +1,52 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Option.h
+
+Abstract:
+
+ IP4 option support routines.
+
+
+**/
+
+#ifndef __EFI_IP4_OPTION_H__
+#define __EFI_IP4_OPTION_H__
+
+enum {
+ IP4_OPTION_EOP = 0,
+ IP4_OPTION_NOP = 1,
+ IP4_OPTION_LSRR = 131, // Loss source and record routing, 10000011
+ IP4_OPTION_SSRR = 137, // Strict source and record routing, 10001001
+ IP4_OPTION_RR = 7, // Record routing, 00000111
+
+ IP4_OPTION_COPY_MASK = 0x80,
+};
+
+BOOLEAN
+Ip4OptionIsValid (
+ IN UINT8 *Option,
+ IN UINT32 OptLen,
+ IN BOOLEAN Rcvd
+ );
+
+EFI_STATUS
+Ip4CopyOption (
+ IN UINT8 *Option,
+ IN UINT32 OptLen,
+ IN BOOLEAN Fragment,
+ IN UINT8 *Buf, OPTIONAL
+ IN OUT UINT32 *BufLen
+ );
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c
new file mode 100644
index 0000000000..d595ab2999
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c
@@ -0,0 +1,457 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Ip4Output.c
+
+Abstract:
+
+ Transmit the IP4 packet
+
+
+**/
+
+#include "Ip4Impl.h"
+
+UINT16 mIp4Id;
+
+
+/**
+ Prepend an IP4 head to the Packet. It will copy the options and
+ build the IP4 header fields. Used for IP4 fragmentation.
+
+ @param Packet The packet to prepend IP4 header to
+ @param Head The caller supplied header. The caller should set
+ the following header fields: Tos, TotalLen, Id,
+ Fragment, Ttl, Protocol, Src and Dst. All the fields
+ are in host byte order. This function will fill in
+ the Ver, HeadLen, and checksum.
+ @param Option The orginal IP4 option to copy from
+ @param OptLen The length of the IP4 option
+
+ @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of
+ Packet.
+ @retval EFI_SUCCESS The IP4 header is successfully added to the packet.
+
+**/
+EFI_STATUS
+Ip4PrependHead (
+ IN NET_BUF *Packet,
+ IN IP4_HEAD *Head,
+ IN UINT8 *Option,
+ IN UINT32 OptLen
+ )
+{
+ UINT32 HeadLen;
+ UINT32 Len;
+ IP4_HEAD *PacketHead;
+ BOOLEAN FirstFragment;
+
+ //
+ // Prepend the options: first get the option length, then copy it over.
+ //
+ HeadLen = 0;
+ FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment);
+
+ Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len);
+
+ HeadLen = IP4_MIN_HEADLEN + Len;
+ ASSERT (((Len %4) == 0) && (HeadLen <= IP4_MAX_HEADLEN));
+
+ PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
+
+ if (PacketHead == NULL) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len);
+
+ //
+ // Set the head up, convert the host byte order to network byte order
+ //
+ PacketHead->Ver = 4;
+ PacketHead->HeadLen = (UINT8) (HeadLen >> 2);
+ PacketHead->Tos = Head->Tos;
+ PacketHead->TotalLen = HTONS (Packet->TotalSize);
+ PacketHead->Id = HTONS (Head->Id);
+ PacketHead->Fragment = HTONS (Head->Fragment);
+ PacketHead->Checksum = 0;
+ PacketHead->Ttl = Head->Ttl;
+ PacketHead->Protocol = Head->Protocol;
+ PacketHead->Src = HTONL (Head->Src);
+ PacketHead->Dst = HTONL (Head->Dst);
+ PacketHead->Checksum = ~NetblockChecksum ((UINT8 *) PacketHead, HeadLen);
+
+ Packet->Ip = PacketHead;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Select an interface to send the packet generated in the IP4 driver
+ itself, that is, not by the requests of IP4 child's consumer. Such
+ packets include the ICMP echo replies, and other ICMP error packets.
+
+ @param IpSb The IP4 service that wants to send the packets.
+ @param Dst The destination of the packet
+ @param Src The source of the packet
+
+ @return NULL if no proper interface is found, otherwise the interface that
+ @return can be used to send the system packet from.
+
+**/
+IP4_INTERFACE *
+Ip4SelectInterface (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src
+ )
+{
+ IP4_INTERFACE *IpIf;
+ IP4_INTERFACE *Selected;
+ NET_LIST_ENTRY *Entry;
+
+ //
+ // Select the interface the Dst is on if one of the connected
+ // network. Some IP instance may be configured with 0.0.0.0/0,
+ // don't select that interface now.
+ //
+ IpIf = Ip4FindNet (IpSb, Dst);
+
+ if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {
+ return IpIf;
+ }
+
+ //
+ // If source is one of the interface address, select it.
+ //
+ IpIf = Ip4FindInterface (IpSb, Src);
+
+ if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {
+ return IpIf;
+ }
+
+ //
+ // Select a configured interface as the fall back. Always prefer
+ // an interface with non-zero address.
+ //
+ Selected = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
+
+ if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) {
+ Selected = IpIf;
+ }
+ }
+
+ return Selected;
+}
+
+
+/**
+ The default callback function for system generated packet.
+ It will free the packet.
+
+ @param Ip4Instance The IP4 child that issued the transmission. It most
+ like is NULL.
+ @param Packet The packet that transmitted.
+ @param IoStatus The result of the transmission, succeeded or failed.
+ @param LinkFlag Not used when transmission. check IP4_FRAME_CALLBACK
+ for reference.
+ @param Context The context provided by us
+
+ @return None
+
+**/
+VOID
+Ip4SysPacketSent (
+ IP4_PROTOCOL *Ip4Instance,
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 LinkFlag,
+ VOID *Context
+ )
+{
+ NetbufFree (Packet);
+}
+
+
+/**
+ Transmit an IP4 packet. The packet comes either from the IP4
+ child's consumer (IpInstance != NULL) or the IP4 driver itself
+ (IpInstance == NULL). It will route the packet, fragment it,
+ then transmit all the fragments through some interface.
+
+ @param IpSb The IP4 service instance to transmit the packet
+ @param IpInstance The IP4 child that issues the transmission. It is
+ NULL if the packet is from the system.
+ @param Packet The user data to send, excluding the IP header.
+ @param Head The caller supplied header. The caller should set
+ the following header fields: Tos, TotalLen, Id, tl,
+ Fragment, Protocol, Src and Dst. All the fields are
+ in host byte order. This function will fill in the
+ Ver, HeadLen, Fragment, and checksum. The Fragment
+ only need to include the DF flag. Ip4Output will
+ compute the MF and offset for you.
+ @param Option The original option to append to the IP headers
+ @param OptLen The length of the option
+ @param GateWay The next hop address to transmit packet to.
+ 255.255.255.255 means broadcast.
+ @param Callback The callback function to issue when transmission
+ completed.
+ @param Context The opaque context for the callback
+
+ @retval EFI_NO_MAPPING There is no interface to the destination.
+ @retval EFI_NOT_FOUND There is no route to the destination
+ @retval EFI_SUCCESS The packet is successfully transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Ip4Output (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_PROTOCOL *IpInstance, OPTIONAL
+ IN NET_BUF *Packet,
+ IN IP4_HEAD *Head,
+ IN UINT8 *Option,
+ IN UINT32 OptLen,
+ IN IP4_ADDR GateWay,
+ IN IP4_FRAME_CALLBACK Callback,
+ IN VOID *Context
+ )
+{
+ IP4_INTERFACE *IpIf;
+ IP4_ROUTE_CACHE_ENTRY *CacheEntry;
+ IP4_ADDR Dest;
+ EFI_STATUS Status;
+ NET_BUF *Fragment;
+ UINT32 Index;
+ UINT32 HeadLen;
+ UINT32 PacketLen;
+ UINT32 Offset;
+ UINT32 Mtu;
+ UINT32 Num;
+
+ //
+ // Select an interface/source for system packet, application
+ // should select them itself.
+ //
+ if (IpInstance == NULL) {
+ IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src);
+ } else {
+ IpIf = IpInstance->Interface;
+ }
+
+ if (IpIf == NULL) {
+ return EFI_NO_MAPPING;
+ }
+
+ if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) {
+ Head->Src = IpIf->Ip;
+ }
+
+ //
+ // Route the packet unless overrided, that is, GateWay isn't zero.
+ //
+ if (GateWay == IP4_ALLZERO_ADDRESS) {
+ Dest = Head->Dst;
+
+ if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) {
+ //
+ // Set the gateway to local broadcast if the Dest is
+ // is the broadcast address for the connected network
+ // or it is local broadcast.
+ //
+ GateWay = IP4_ALLONE_ADDRESS;
+
+ } else if (IP4_IS_MULTICAST (Dest)) {
+ //
+ // Set the gateway to the destination if it is an multicast
+ // address. The IP4_INTERFACE won't consult ARP to send local
+ // broadcast and multicast.
+ //
+ GateWay = Head->Dst;
+
+ } else {
+ //
+ // Consult the route table to route the packet
+ //
+ if (IpInstance == NULL) {
+ CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);
+ } else {
+ CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src);
+ }
+
+ if (CacheEntry == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ GateWay = CacheEntry->NextHop;
+ Ip4FreeRouteCacheEntry (CacheEntry);
+ }
+ }
+
+ //
+ // OK, selected the source and route, fragment the packet then send
+ // them. Tag each fragment other than the first one as spawn from it.
+ //
+ Mtu = IpSb->SnpMode.MaxPacketSize;
+ HeadLen = sizeof (IP4_HEAD) + (OptLen + 3) &~0x03;
+ Head->Id = mIp4Id++;
+
+ if (Packet->TotalSize + HeadLen > Mtu) {
+ //
+ // Packet is fragmented from the tail to the head, that is, the
+ // first frame sent is the last fragment of the packet. The first
+ // fragment is NOT sent in this loop. First compute how many
+ // fragments there are.
+ //
+ Mtu = (Mtu - HeadLen) & (~0x07);
+ Num = (Packet->TotalSize + Mtu - 1) / Mtu;
+
+ //
+ // Initialize the packet length and Offset. Other than the last
+ // fragment, the packet length equals to MTU. The offset is always
+ // aligned to MTU.
+ //
+ PacketLen = Packet->TotalSize - (Num - 1) * Mtu;
+ Offset = Mtu * (Num - 1);
+
+ for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) {
+ Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN);
+
+ if (Fragment == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Update the header's fragment. The caller fills the IP4 header
+ // fields that are required by Ip4PrependHead except the fragment.
+ //
+ Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset);
+ Ip4PrependHead (Fragment, Head, Option, OptLen);
+
+ //
+ // Transmit the fragments, pass the Packet address as the context.
+ // So, we can find all the fragments spawned from the Packet by
+ // compare the NetBuf and Context to the Packet.
+ //
+ Status = Ip4SendFrame (
+ IpIf,
+ IpInstance,
+ Fragment,
+ GateWay,
+ Ip4SysPacketSent,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ PacketLen = Mtu;
+ }
+
+ //
+ // Trim the already sent data, then adjust the head's fragment field.
+ //
+ NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE);
+ Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0);
+ }
+
+ //
+ // Send the first fragment, it is either the orginal packet, or the
+ // first fragment of a fragmented packet. It seems that there is a subtle
+ // bug here: what if the caller free the packet in Callback and IpIf (or
+ // MNP child used by that interface) still holds the fragments and try
+ // to access the data? The caller can free the packet if it recycles the
+ // consumer's (such as UDP) data in the Callback. But this can't happen.
+ // The detailed sequence is:
+ // 1. for the packets generated by IP4 driver itself:
+ // The Callback is Ip4SysPacketSent, which is the same as the
+ // fragments' callback. Ip4SysPacketSent simply calls NetbufFree
+ // to release its reference to the packet. So, no problem for
+ // system packets.
+ //
+ // 2. for the upper layer's packets (use UDP as an example):
+ // UDP requests the IP layer to transmit some data which is
+ // wrapped in an asynchronous token, the token is wrapped
+ // in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data
+ // in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP
+ // is bound with the Packet. It will only be freed when all
+ // the references to Packet have been released. Upon then, the
+ // Packet's OnFree callback will release the IP4_TXTOKEN_WRAP,
+ // and singal the user's recycle event. So, also no problem for
+ // upper layer's packets.
+ //
+ Ip4PrependHead (Packet, Head, Option, OptLen);
+ Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip4CancelPacket (IpIf, Packet, Status);
+ return Status;
+}
+
+
+/**
+ The filter function to find a packet and all its fragments.
+ The packet's fragments have their Context set to the packet.
+
+ @param Frame The frames hold by the low level interface
+ @param Context Context to the function, which is the packet.
+
+ @retval TRUE This is the packet to cancel or its fragments.
+ @retval FALSE This is unrelated packet.
+
+**/
+STATIC
+BOOLEAN
+Ip4CancelPacketFragments (
+ IP4_LINK_TX_TOKEN *Frame,
+ VOID *Context
+ )
+{
+ if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Cancel the Packet and all its fragments.
+
+ @param IpIf The interface from which the Packet is sent
+ @param Packet The Packet to cancel
+ @param IoStatus The status returns to the sender.
+
+ @return None
+
+**/
+VOID
+Ip4CancelPacket (
+ IN IP4_INTERFACE *IpIf,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus
+ )
+{
+ Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h
new file mode 100644
index 0000000000..e165e1d715
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h
@@ -0,0 +1,54 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Ip4Output.h
+
+Abstract:
+
+
+**/
+
+#ifndef __EFI_IP4_OUTPUT_H__
+#define __EFI_IP4_OUTPUT_H__
+
+VOID
+Ip4SysPacketSent (
+ IP4_PROTOCOL *Ip4Instance,
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 Flag,
+ VOID *Context
+ );
+
+EFI_STATUS
+Ip4Output (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_PROTOCOL *IpInstance, OPTIONAL
+ IN NET_BUF *Data,
+ IN IP4_HEAD *Head,
+ IN UINT8 *Option,
+ IN UINT32 OptLen,
+ IN IP4_ADDR GateWay,
+ IN IP4_FRAME_CALLBACK Callback,
+ IN VOID *Context
+ );
+
+VOID
+Ip4CancelPacket (
+ IN IP4_INTERFACE *IpIf,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus
+ );
+
+extern UINT16 mIp4Id;
+#endif
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c
new file mode 100644
index 0000000000..2cf1d0f29c
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c
@@ -0,0 +1,690 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Route.c
+
+Abstract:
+
+
+**/
+
+#include "Ip4Impl.h"
+
+
+/**
+ Allocate a route entry then initialize it with the Dest/Netmaks
+ and Gateway.
+
+ @param Dest The destination network
+ @param Netmask The destination network mask
+ @param GateWay The nexthop address
+
+ @return NULL if failed to allocate memeory, otherwise the newly created
+ @return route entry.
+
+**/
+STATIC
+IP4_ROUTE_ENTRY *
+Ip4CreateRouteEntry (
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR GateWay
+ )
+{
+ IP4_ROUTE_ENTRY *RtEntry;
+
+ RtEntry = NetAllocatePool (sizeof (IP4_ROUTE_ENTRY));
+
+ if (RtEntry == NULL) {
+ return NULL;
+ }
+
+ NetListInit (&RtEntry->Link);
+
+ RtEntry->RefCnt = 1;
+ RtEntry->Dest = Dest;
+ RtEntry->Netmask = Netmask;
+ RtEntry->NextHop = GateWay;
+ RtEntry->Flag = 0;
+
+ return RtEntry;
+}
+
+
+/**
+ Free the route table entry. It is reference counted.
+
+ @param RtEntry The route entry to free.
+
+ @return NONE
+
+**/
+STATIC
+VOID
+Ip4FreeRouteEntry (
+ IN IP4_ROUTE_ENTRY *RtEntry
+ )
+{
+ ASSERT (RtEntry->RefCnt > 0);
+
+ if (--RtEntry->RefCnt == 0) {
+ NetFreePool (RtEntry);
+ }
+}
+
+
+/**
+ Allocate and initialize a IP4 route cache entry.
+
+ @param Dst The destination address
+ @param Src The source address
+ @param GateWay The next hop address
+ @param Tag The tag from the caller. This marks all the cache
+ entries spawned from one route table entry.
+
+ @return NULL if failed to allocate memory for the cache, other point
+ @return to the created route cache entry.
+
+**/
+STATIC
+IP4_ROUTE_CACHE_ENTRY *
+Ip4CreateRouteCacheEntry (
+ IN IP4_ADDR Dst,
+ IN IP4_ADDR Src,
+ IN IP4_ADDR GateWay,
+ IN UINTN Tag
+ )
+{
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+
+ RtCacheEntry = NetAllocatePool (sizeof (IP4_ROUTE_CACHE_ENTRY));
+
+ if (RtCacheEntry == NULL) {
+ return NULL;
+ }
+
+ NetListInit (&RtCacheEntry->Link);
+
+ RtCacheEntry->RefCnt = 1;
+ RtCacheEntry->Dest = Dst;
+ RtCacheEntry->Src = Src;
+ RtCacheEntry->NextHop = GateWay;
+ RtCacheEntry->Tag = Tag;
+
+ return RtCacheEntry;
+}
+
+
+/**
+ Free the route cache entry. It is reference counted.
+
+ @param RtCacheEntry The route cache entry to free.
+
+ @return None
+
+**/
+VOID
+Ip4FreeRouteCacheEntry (
+ IN IP4_ROUTE_CACHE_ENTRY *RtCacheEntry
+ )
+{
+ ASSERT (RtCacheEntry->RefCnt > 0);
+
+ if (--RtCacheEntry->RefCnt == 0) {
+ NetFreePool (RtCacheEntry);
+ }
+}
+
+
+/**
+ Initialize an empty route cache table.
+
+ @param RtCache The rotue cache table to initialize.
+
+ @return NONE
+
+**/
+VOID
+Ip4InitRouteCache (
+ IN IP4_ROUTE_CACHE *RtCache
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {
+ NetListInit (&(RtCache->CacheBucket[Index]));
+ }
+}
+
+
+/**
+ Clean up a route cache, that is free all the route cache
+ entries enqueued in the cache.
+
+ @param RtCache The route cache table to clean up
+
+ @return None
+
+**/
+VOID
+Ip4CleanRouteCache (
+ IN IP4_ROUTE_CACHE *RtCache
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtCache->CacheBucket[Index])) {
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);
+
+ NetListRemoveEntry (Entry);
+ Ip4FreeRouteCacheEntry (RtCacheEntry);
+ }
+ }
+}
+
+
+
+/**
+ Create an empty route table, includes its internal route cache
+
+ None
+
+ @return NULL if failed to allocate memory for the route table, otherwise
+ @return the point to newly created route table.
+
+**/
+IP4_ROUTE_TABLE *
+Ip4CreateRouteTable (
+ VOID
+ )
+{
+ IP4_ROUTE_TABLE *RtTable;
+ UINT32 Index;
+
+ RtTable = NetAllocatePool (sizeof (IP4_ROUTE_TABLE));
+
+ if (RtTable == NULL) {
+ return NULL;
+ }
+
+ RtTable->RefCnt = 1;
+ RtTable->TotalNum = 0;
+
+ for (Index = 0; Index < IP4_MASK_NUM; Index++) {
+ NetListInit (&(RtTable->RouteArea[Index]));
+ }
+
+ RtTable->Next = NULL;
+
+ Ip4InitRouteCache (&RtTable->Cache);
+ return RtTable;
+}
+
+
+/**
+ Free the route table and its associated route cache. Route
+ table is reference counted.
+
+ @param RtTable The route table to free.
+
+ @return None
+
+**/
+VOID
+Ip4FreeRouteTable (
+ IN IP4_ROUTE_TABLE *RtTable
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_ROUTE_ENTRY *RtEntry;
+ UINT32 Index;
+
+ ASSERT (RtTable->RefCnt > 0);
+
+ if (--RtTable->RefCnt > 0) {
+ return ;
+ }
+
+ //
+ // Free all the route table entry and its route cache.
+ //
+ for (Index = 0; Index < IP4_MASK_NUM; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtTable->RouteArea[Index])) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ NetListRemoveEntry (Entry);
+ Ip4FreeRouteEntry (RtEntry);
+ }
+ }
+
+ Ip4CleanRouteCache (&RtTable->Cache);
+
+ NetFreePool (RtTable);
+}
+
+
+
+/**
+ Remove all the cache entries bearing the Tag. When a route cache
+ entry is created, it is tagged with the address of route entry
+ from which it is spawned. When a route entry is deleted, the cache
+ entries spawned from it are also deleted.
+
+ @param RtCache Route cache to remove the entries from
+ @param Tag The Tag of the entries to remove
+
+ @return None
+
+**/
+STATIC
+VOID
+Ip4PurgeRouteCache (
+ IN IP4_ROUTE_CACHE *RtCache,
+ IN UINTN Tag
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {
+
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);
+
+ if (RtCacheEntry->Tag == Tag) {
+ NetListRemoveEntry (Entry);
+ Ip4FreeRouteCacheEntry (RtCacheEntry);
+ }
+ }
+ }
+}
+
+
+/**
+ Add a route entry to the route table. All the IP4_ADDRs are in
+ host byte order.
+
+ @param RtTable Route table to add route to
+ @param Dest The destination of the network
+ @param Netmask The netmask of the destination
+ @param Gateway The next hop address
+
+ @retval EFI_ACCESS_DENIED The same route already exists
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry
+ @retval EFI_SUCCESS The route is added successfully.
+
+**/
+EFI_STATUS
+Ip4AddRoute (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR Gateway
+ )
+{
+ NET_LIST_ENTRY *Head;
+ NET_LIST_ENTRY *Entry;
+ IP4_ROUTE_ENTRY *RtEntry;
+
+ //
+ // All the route entries with the same netmask length are
+ // linke to the same route area
+ //
+ Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]);
+
+ //
+ // First check whether the route exists
+ //
+ NET_LIST_FOR_EACH (Entry, Head) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) {
+ return EFI_ACCESS_DENIED;
+ }
+ }
+
+ //
+ // Create a route entry and insert it to the route area.
+ //
+ RtEntry = Ip4CreateRouteEntry (Dest, Netmask, Gateway);
+
+ if (RtEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (Gateway == IP4_ALLZERO_ADDRESS) {
+ RtEntry->Flag = IP4_DIRECT_ROUTE;
+ }
+
+ NetListInsertHead (Head, &RtEntry->Link);
+ RtTable->TotalNum++;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Remove a route entry and all the route caches spawn from it.
+
+ @param RtTable The route table to remove the route from
+ @param Dest The destination network
+ @param Netmask The netmask of the Dest
+ @param Gateway The next hop address
+
+ @retval EFI_SUCCESS The route entry is successfully removed
+ @retval EFI_NOT_FOUND There is no route entry in the table with that
+ properity.
+
+**/
+EFI_STATUS
+Ip4DelRoute (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR Gateway
+ )
+{
+ NET_LIST_ENTRY *Head;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_ROUTE_ENTRY *RtEntry;
+
+ Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]);
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) {
+ Ip4PurgeRouteCache (&RtTable->Cache, (UINTN) RtEntry);
+ NetListRemoveEntry (Entry);
+ Ip4FreeRouteEntry (RtEntry);
+
+ RtTable->TotalNum--;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Find a route cache with the dst and src. This is used by ICMP
+ redirect messasge process. All kinds of redirect is treated as
+ host redirect according to RFC1122. So, only route cache entries
+ are modified according to the ICMP redirect message.
+
+ @param RtTable The route table to search the cache for
+ @param Dest The destination address
+ @param Src The source address
+
+ @return NULL if no route entry to the (Dest, Src). Otherwise the point
+ @return to the correct route cache entry.
+
+**/
+IP4_ROUTE_CACHE_ENTRY *
+Ip4FindRouteCache (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Src
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ Index = IP4_ROUTE_CACHE_HASH (Dest, Src);
+
+ NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);
+
+ if ((RtCacheEntry->Dest == Dest) && (RtCacheEntry->Src == Src)) {
+ NET_GET_REF (RtCacheEntry);
+ return RtCacheEntry;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Search the route table for a most specific match to the Dst. It searches
+ from the longest route area (mask length == 32) to the shortest route area (
+ default routes). In each route area, it will first search the instance's
+ route table, then the default route table. This is required by the following
+ requirements:
+ 1. IP search the route table for a most specific match
+ 2. The local route entries have precedence over the default route entry.
+
+ @param RtTable The route table to search from
+ @param Dst The destionation address to search
+
+ @return NULL if no route matches the Dst, otherwise the point to the
+ @return most specific route to the Dst.
+
+**/
+STATIC
+IP4_ROUTE_ENTRY *
+Ip4FindRouteEntry (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dst
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ IP4_ROUTE_ENTRY *RtEntry;
+ IP4_ROUTE_TABLE *Table;
+ INTN Index;
+
+ RtEntry = NULL;
+
+ for (Index = IP4_MASK_NUM - 1; Index >= 0; Index--) {
+ for (Table = RtTable; Table != NULL; Table = Table->Next) {
+ NET_LIST_FOR_EACH (Entry, &Table->RouteArea[Index]) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ if (IP4_NET_EQUAL (RtEntry->Dest, Dst, RtEntry->Netmask)) {
+ NET_GET_REF (RtEntry);
+ return RtEntry;
+ }
+ }
+ }
+ }
+
+
+ return NULL;
+}
+
+
+/**
+ Search the route table to route the packet. Return/creat a route
+ cache if there is a route to the destination.
+
+ @param RtTable The route table to search from
+ @param Dest The destination address to search for
+ @param Src The source address to search for
+
+ @return NULL if failed to route packet, otherwise a route cache
+ @return entry that can be used to route packet.
+
+**/
+IP4_ROUTE_CACHE_ENTRY *
+Ip4Route (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Src
+ )
+{
+ NET_LIST_ENTRY *Head;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ IP4_ROUTE_CACHE_ENTRY *Cache;
+ IP4_ROUTE_ENTRY *RtEntry;
+ IP4_ADDR NextHop;
+ UINT32 Count;
+
+ ASSERT (RtTable != NULL);
+
+ Head = &RtTable->Cache.CacheBucket[IP4_ROUTE_CACHE_HASH (Dest, Src)];
+ RtCacheEntry = Ip4FindRouteCache (RtTable, Dest, Src);
+
+ //
+ // If found, promote the cache entry to the head of the hash bucket. LRU
+ //
+ if (RtCacheEntry != NULL) {
+ NetListRemoveEntry (&RtCacheEntry->Link);
+ NetListInsertHead (Head, &RtCacheEntry->Link);
+ return RtCacheEntry;
+ }
+
+ //
+ // Search the route table for the most specific route
+ //
+ RtEntry = Ip4FindRouteEntry (RtTable, Dest);
+
+ if (RtEntry == NULL) {
+ return NULL;
+ }
+
+ //
+ // Found a route to the Dest, if it is a direct route, the packet
+ // will be send directly to the destination, such as for connected
+ // network. Otherwise, it is an indirect route, the packet will be
+ // send the next hop router.
+ //
+ if (RtEntry->Flag & IP4_DIRECT_ROUTE) {
+ NextHop = Dest;
+ } else {
+ NextHop = RtEntry->NextHop;
+ }
+
+ Ip4FreeRouteEntry (RtEntry);
+
+ //
+ // Create a route cache entry, and tag it as spawned from this route entry
+ //
+ RtCacheEntry = Ip4CreateRouteCacheEntry (Dest, Src, NextHop, (UINTN) RtEntry);
+
+ if (RtCacheEntry == NULL) {
+ return NULL;
+ }
+
+ NetListInsertHead (Head, &RtCacheEntry->Link);
+ NET_GET_REF (RtCacheEntry);
+
+ //
+ // Each bucket of route cache can contain at most 64 entries.
+ // Remove the entries at the tail of the bucket. These entries
+ // are likely to be used least.
+ //
+ Count = 0;
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
+ if (++Count < IP4_ROUTE_CACHE_MAX) {
+ continue;
+ }
+
+ Cache = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);
+
+ NetListRemoveEntry (Entry);
+ Ip4FreeRouteCacheEntry (Cache);
+ }
+
+ return RtCacheEntry;
+}
+
+
+/**
+ Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of
+ GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the
+ internal operation of the IP4 driver.
+
+ @param IpInstance The IP4 child that requests the route table.
+
+ @retval EFI_SUCCESS The route table is successfully build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the rotue table.
+
+**/
+EFI_STATUS
+Ip4BuildEfiRouteTable (
+ IN IP4_PROTOCOL *IpInstance
+ )
+{
+ IP4_SERVICE *IpSb;
+ NET_LIST_ENTRY *Entry;
+ IP4_ROUTE_TABLE *RtTable;
+ IP4_ROUTE_ENTRY *RtEntry;
+ EFI_IP4_ROUTE_TABLE *Table;
+ UINT32 Count;
+ INT32 Index;
+
+ IpSb = IpInstance->Service;
+ RtTable = IpInstance->RouteTable;
+
+ if (IpInstance->EfiRouteTable != NULL) {
+ NetFreePool (IpInstance->EfiRouteTable);
+
+ IpInstance->EfiRouteTable = NULL;
+ IpInstance->EfiRouteCount = 0;
+ }
+
+ Count = RtTable->TotalNum;
+
+ if (RtTable->Next != NULL) {
+ Count += RtTable->Next->TotalNum;
+ }
+
+ if (Count == 0) {
+ return EFI_SUCCESS;
+ }
+
+ Table = NetAllocatePool (sizeof (EFI_IP4_ROUTE_TABLE) * Count);
+
+ if (Table == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy the route entry to EFI route table. Keep the order of
+ // route entry copied from most specific to default route. That
+ // is, interlevel the route entry from the instance's route area
+ // and those from the default route table's route area.
+ //
+ Count = 0;
+
+ for (Index = IP4_MASK_NUM - 1; Index >= 0; Index--) {
+ for (RtTable = IpInstance->RouteTable; RtTable != NULL; RtTable = RtTable->Next) {
+ NET_LIST_FOR_EACH (Entry, &(RtTable->RouteArea[Index])) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);
+
+ EFI_IP4 (Table[Count].SubnetAddress) = HTONL (RtEntry->Dest & RtEntry->Netmask);
+ EFI_IP4 (Table[Count].SubnetMask) = HTONL (RtEntry->Netmask);
+ EFI_IP4 (Table[Count].GatewayAddress) = HTONL (RtEntry->NextHop);
+
+ Count++;
+ }
+ }
+ }
+
+ IpInstance->EfiRouteTable = Table;
+ IpInstance->EfiRouteCount = Count;
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h
new file mode 100644
index 0000000000..ce07b1d10c
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h
@@ -0,0 +1,151 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+Module Name:
+
+ Ip4Route.h
+
+Abstract:
+
+ EFI IP4 route table and route cache table defintions.
+
+
+**/
+
+#ifndef __EFI_IP4_ROUTE_H__
+#define __EFI_IP4_ROUTE_H__
+
+#include "IP4Common.h"
+
+enum {
+ IP4_DIRECT_ROUTE = 0x00000001,
+
+ IP4_ROUTE_CACHE_HASH = 31,
+ IP4_ROUTE_CACHE_MAX = 64, // Max NO. of cache entry per hash bucket
+};
+
+#define IP4_ROUTE_CACHE_HASH(Dst, Src) (((Dst) ^ (Src)) % IP4_ROUTE_CACHE_HASH)
+
+//
+// The route entry in the route table. Dest/Netmask is the destion
+// network. The nexthop is the gateway to send the packet to in
+// order to reach the Dest/Netmask. If the Flag has IP4_DIRECT_ROUTE
+// on, the gateway is the destination of the IP packet itself. Route
+// enties of the connected network have the flag on.
+//
+typedef struct {
+ NET_LIST_ENTRY Link;
+ INTN RefCnt;
+ IP4_ADDR Dest;
+ IP4_ADDR Netmask;
+ IP4_ADDR NextHop;
+ UINT32 Flag;
+} IP4_ROUTE_ENTRY;
+
+//
+// The route cache entry. The route cache entry is optional.
+// But it is necessary to support the ICMP redirect message.
+// Check Ip4ProcessIcmpRedirect for information.
+//
+// The cache entry field Tag is used to tag all the route
+// cache entry spawned from a route table entry. This makes
+// it simple to delete all the route cache entries from a
+// to-be-deleted route entry.
+//
+typedef struct {
+ NET_LIST_ENTRY Link;
+ INTN RefCnt;
+ IP4_ADDR Dest;
+ IP4_ADDR Src;
+ IP4_ADDR NextHop;
+ UINTN Tag;
+} IP4_ROUTE_CACHE_ENTRY;
+
+//
+// The route cache table is organized as a hash table. Each
+// IP4 route table has a embedded route cache. For now the
+// route cache and route table are binded togehter. But keep
+// the route cache a seperated structure in case we want to
+// detach them later.
+//
+typedef struct {
+ NET_LIST_ENTRY CacheBucket[IP4_ROUTE_CACHE_HASH];
+} IP4_ROUTE_CACHE;
+
+//
+// Each IP4 instance has its own route table. Each ServiceBinding
+// instance has a default route table and default address.
+//
+// All the route table entries with the same mask are linked
+// together in one route area. For example, RouteArea[0] contains
+// the default routes. A route table also contains a route cache.
+//
+typedef struct _IP4_ROUTE_TABLE IP4_ROUTE_TABLE;
+
+typedef struct _IP4_ROUTE_TABLE {
+ INTN RefCnt;
+ UINT32 TotalNum;
+ NET_LIST_ENTRY RouteArea[IP4_MASK_NUM];
+ IP4_ROUTE_TABLE *Next;
+ IP4_ROUTE_CACHE Cache;
+};
+
+IP4_ROUTE_TABLE*
+Ip4CreateRouteTable (
+ VOID
+ );
+
+VOID
+Ip4FreeRouteTable (
+ IN IP4_ROUTE_TABLE *RouteTable
+ );
+
+EFI_STATUS
+Ip4AddRoute (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR Gateway
+ );
+
+EFI_STATUS
+Ip4DelRoute (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Netmask,
+ IN IP4_ADDR Gateway
+ );
+
+IP4_ROUTE_CACHE_ENTRY *
+Ip4FindRouteCache (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Src
+ );
+
+VOID
+Ip4FreeRouteCacheEntry (
+ IN IP4_ROUTE_CACHE_ENTRY *RtCacheEntry
+ );
+
+IP4_ROUTE_CACHE_ENTRY *
+Ip4Route (
+ IN IP4_ROUTE_TABLE *RtTable,
+ IN IP4_ADDR Dest,
+ IN IP4_ADDR Src
+ );
+
+EFI_STATUS
+Ip4BuildEfiRouteTable (
+ IN IP4_PROTOCOL *IpInstance
+ );
+#endif
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c
index 5a648f5ffc..83fc3c6ae4 100644
--- a/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c
@@ -139,7 +139,7 @@ MnpAddFreeNbuf (
for (Index = 0; Index < Count; Index++) {
- Nbuf = NetbufAlloc (MnpServiceData->BufferLength);
+ Nbuf = NetbufAlloc (MnpServiceData->BufferLength + MnpServiceData->PaddingSize);
if (Nbuf == NULL) {
MNP_DEBUG_ERROR (("MnpAddFreeNbuf: NetBufAlloc failed.\n"));
@@ -147,6 +147,14 @@ MnpAddFreeNbuf (
break;
}
+ if (MnpServiceData->PaddingSize > 0) {
+ //
+ // Pad padding bytes before the media header
+ //
+ NetbufAllocSpace (Nbuf, MnpServiceData->PaddingSize, NET_BUF_TAIL);
+ NetbufTrim (Nbuf, MnpServiceData->PaddingSize, NET_BUF_HEAD);
+ }
+
NetbufQueAppend (&MnpServiceData->FreeNbufQue, Nbuf);
}
@@ -329,6 +337,12 @@ MnpInitializeServiceData (
MnpServiceData->BufferLength = MnpServiceData->Mtu + SnpMode->MediaHeaderSize + NET_ETHER_FCS_SIZE;
//
+ // Make sure the protocol headers immediately following the media header
+ // 4-byte aligned
+ //
+ MnpServiceData->PaddingSize = (4 - SnpMode->MediaHeaderSize) & 0x3;
+
+ //
// Initialize the FreeNetBufQue and pre-allocate some NET_BUFs.
//
NetbufQueInit (&MnpServiceData->FreeNbufQue);
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c
index 07be21dced..932b21c5ef 100644
--- a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c
@@ -521,7 +521,6 @@ MnpServiceBindingDestroyChild (
return Status;
}
-//@MT: EFI_DRIVER_ENTRY_POINT (MnpDriverEntryPoint)
EFI_STATUS
EFIAPI
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h
index 48ad0960cd..a387eb05d0 100644
--- a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h
@@ -82,6 +82,7 @@ typedef struct _MNP_SERVICE_DATA {
// store a packet.
//
UINT32 BufferLength;
+ UINT32 PaddingSize;
NET_BUF *RxNbufCache;
UINT8 *TxBuf;
} MNP_SERVICE_DATA;
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c
index 91b8d4e0b8..aafc8433a5 100644
--- a/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c
@@ -974,7 +974,10 @@ MnpReceivePacket (
//
// No receiver for this packet.
//
- NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL);
+ if (Trimmed > 0) {
+ NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL);
+ }
+
goto EXIT;
}
//
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..258299418c
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c
@@ -0,0 +1,161 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+#include "Mtftp4Driver.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+Mtftp4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+Mtftp4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName = {
+ Mtftp4ComponentNameGetDriverName,
+ Mtftp4ComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mMtftp4DriverNameTable[] = {
+ {
+ "eng",
+ L"MTFTP4 Network Service"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+Mtftp4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This : A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language : A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName : A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCES : The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER : Language is NULL.
+ EFI_INVALID_PARAMETER : DriverName is NULL.
+ EFI_UNSUPPORTED : The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gMtftp4ComponentName.SupportedLanguages,
+ mMtftp4DriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+Mtftp4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This : A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle :The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle :The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language : A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName : A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+ Returns:
+ EFI_SUCCESS :The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER : ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER : ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER : Language is NULL.
+ EFI_INVALID_PARAMETER : ControllerName is NULL.
+ EFI_UNSUPPORTED : The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ EFI_UNSUPPORTED :The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c
new file mode 100644
index 0000000000..9b30b3d730
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c
@@ -0,0 +1,643 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Driver.c
+
+Abstract:
+
+
+**/
+
+#include "Mtftp4Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding = {
+ Mtftp4DriverBindingSupported,
+ Mtftp4DriverBindingStart,
+ Mtftp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL gMtftp4ServiceBindingTemplete = {
+ Mtftp4ServiceBindingCreateChild,
+ Mtftp4ServiceBindingDestroyChild
+};
+
+//@MT: EFI_DRIVER_ENTRY_POINT (Mtftp4DriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+Mtftp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ The driver entry point which installs multiple protocols to the ImageHandle.
+
+Arguments:
+
+ ImageHandle - The MTFTP's image handle
+ SystemTable - The system table
+
+Returns:
+
+ EFI_SUCCESS - The handles are successfully installed on the image. Otherwise
+ some EFI_ERROR.
+
+--*/
+{
+ return NetLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gMtftp4DriverBinding,
+ ImageHandle,
+ &gMtftp4ComponentName,
+ NULL,
+ NULL
+ );
+}
+
+
+/**
+ Test whether MTFTP driver support this controller.
+
+ @param This The MTFTP driver binding instance
+ @param Controller The controller to test
+ @param RemainingDevicePath The remaining device path
+
+ @retval EFI_SUCCESS The controller has UDP service binding protocol
+ installed, MTFTP can support it.
+ @retval Others MTFTP can't support the controller.
+
+**/
+EFI_STATUS
+Mtftp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Config a NULL UDP that is used to keep the connection between UDP
+ and MTFTP. Just leave the Udp child unconfigured. When UDP is
+ unloaded, MTFTP will be informed with DriverBinding Stop.
+
+ @param UdpIo The UDP port to configure
+ @param Context The opaque parameter to the callback
+
+ @retval EFI_SUCCESS It always return EFI_SUCCESS directly.
+
+**/
+EFI_STATUS
+Mtftp4ConfigNullUdp (
+ IN UDP_IO_PORT *UdpIo,
+ IN VOID *Context
+ )
+{
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Create then initialize a MTFTP service binding instance.
+
+ @param Controller The controller to install the MTFTP service
+ binding on
+ @param Image The driver binding image of the MTFTP driver
+ @param Service The variable to receive the created service
+ binding instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the instance
+ @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep
+ connection with UDP.
+ @retval EFI_SUCCESS The service instance is created for the
+ controller.
+
+**/
+EFI_STATUS
+Mtftp4CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ OUT MTFTP4_SERVICE **Service
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ EFI_STATUS Status;
+
+ *Service = NULL;
+ MtftpSb = NetAllocatePool (sizeof (MTFTP4_SERVICE));
+
+ if (MtftpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MtftpSb->Signature = MTFTP4_SERVICE_SIGNATURE;
+ MtftpSb->ServiceBinding = gMtftp4ServiceBindingTemplete;
+ MtftpSb->InDestory = FALSE;
+ MtftpSb->ChildrenNum = 0;
+ NetListInit (&MtftpSb->Children);
+
+ MtftpSb->Timer = NULL;
+ MtftpSb->TimerToGetMap = NULL;
+ MtftpSb->Controller = Controller;
+ MtftpSb->Image = Image;
+ MtftpSb->ConnectUdp = NULL;
+
+ //
+ // Create the timer and a udp to be notified when UDP is uninstalled
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Mtftp4OnTimerTick,
+ MtftpSb,
+ &MtftpSb->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetFreePool (MtftpSb);
+ return Status;
+ }
+
+ //
+ // Create the timer used to time out the procedure which is used to
+ // get the default IP address.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &MtftpSb->TimerToGetMap
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (MtftpSb->Timer);
+ NetFreePool (MtftpSb);
+ return Status;
+ }
+
+ MtftpSb->ConnectUdp = UdpIoCreatePort (Controller, Image, Mtftp4ConfigNullUdp, NULL);
+
+ if (MtftpSb->ConnectUdp == NULL) {
+ gBS->CloseEvent (MtftpSb->TimerToGetMap);
+ gBS->CloseEvent (MtftpSb->Timer);
+ NetFreePool (MtftpSb);
+ return EFI_DEVICE_ERROR;
+ }
+
+ *Service = MtftpSb;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Release all the resource used the MTFTP service binding instance.
+
+ @param MtftpSb The MTFTP service binding instance.
+
+ @return None
+
+**/
+VOID
+Mtftp4CleanService (
+ IN MTFTP4_SERVICE *MtftpSb
+ )
+{
+ UdpIoFreePort (MtftpSb->ConnectUdp);
+ gBS->CloseEvent (MtftpSb->TimerToGetMap);
+ gBS->CloseEvent (MtftpSb->Timer);
+}
+
+
+/**
+ Start the MTFTP driver on this controller. MTFTP driver will
+ install a MTFTP SERVICE BINDING protocol on the supported
+ controller, which can be used to create/destroy MTFTP children.
+
+ @param This The MTFTP driver binding protocol.
+ @param Controller The controller to manage.
+ @param RemainingDevicePath Remaining device path.
+
+ @retval EFI_ALREADY_STARTED The MTFTP service binding protocol has been
+ started on the controller.
+ @retval EFI_SUCCESS The MTFTP service binding is installed on the
+ controller.
+
+**/
+EFI_STATUS
+Mtftp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ EFI_STATUS Status;
+
+ //
+ // Directly return if driver is already running.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Mtftp4CreateService (Controller, This->DriverBindingHandle, &MtftpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Mtftp4ServiceBinding Protocol onto Controller
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ &MtftpSb->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Mtftp4CleanService (MtftpSb);
+ NetFreePool (MtftpSb);
+
+ return Status;
+}
+
+
+/**
+ Stop the MTFTP driver on controller. The controller is a UDP
+ child handle.
+
+ @param This The MTFTP driver binding protocol
+ @param Controller The controller to stop
+ @param NumberOfChildren The number of children
+ @param ChildHandleBuffer The array of the child handle.
+
+ @retval EFI_SUCCESS The driver is stopped on the controller.
+ @retval EFI_DEVICE_ERROR Failed to stop the driver on the controller.
+
+**/
+EFI_STATUS
+Mtftp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ MTFTP4_SERVICE *MtftpSb;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // MTFTP driver opens UDP child, So, Controller is a UDP
+ // child handle. Locate the Nic handle first. Then get the
+ // MTFTP private data back.
+ //
+ NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (ServiceBinding);
+
+ if (MtftpSb->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ MtftpSb->InDestory = TRUE;
+
+ while (!NetListIsEmpty (&MtftpSb->Children)) {
+ Instance = NET_LIST_HEAD (&MtftpSb->Children, MTFTP4_PROTOCOL, Link);
+ Mtftp4ServiceBindingDestroyChild (ServiceBinding, Instance->Handle);
+ }
+
+ if (MtftpSb->ChildrenNum != 0) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ Status = gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Mtftp4CleanService (MtftpSb);
+ NetFreePool (MtftpSb);
+
+ NET_RESTORE_TPL (OldTpl);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ MtftpSb->InDestory = FALSE;
+
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Initialize a MTFTP protocol instance which is the child of MtftpSb.
+
+ @param MtftpSb The MTFTP service binding protocol.
+ @param Instance The MTFTP instance to initialize.
+
+ @return None
+
+**/
+VOID
+Mtftp4InitProtocol (
+ IN MTFTP4_SERVICE *MtftpSb,
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ NetZeroMem (Instance, sizeof (MTFTP4_PROTOCOL));
+
+ Instance->Signature = MTFTP4_PROTOCOL_SIGNATURE;
+ NetListInit (&Instance->Link);
+ CopyMem (&Instance->Mtftp4, &gMtftp4ProtocolTemplate, sizeof (EFI_MTFTP4_PROTOCOL));
+ Instance->State = MTFTP4_STATE_UNCONFIGED;
+ Instance->Indestory = FALSE;
+ Instance->Service = MtftpSb;
+
+ NetListInit (&Instance->Blocks);
+}
+
+
+/**
+ Create a MTFTP child for the service binding instance, then
+ install the MTFTP protocol to the ChildHandle.
+
+ @param This The MTFTP service binding instance.
+ @param ChildHandle The Child handle to install the MTFTP protocol.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the new child.
+ @retval EFI_SUCCESS The child is successfully create.
+
+**/
+EFI_STATUS
+Mtftp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ VOID *Udp4;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = NetAllocatePool (sizeof (*Instance));
+
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);
+
+ Mtftp4InitProtocol (MtftpSb, Instance);
+
+ Instance->UnicastPort = UdpIoCreatePort (
+ MtftpSb->Controller,
+ MtftpSb->Image,
+ Mtftp4ConfigNullUdp,
+ Instance
+ );
+
+ if (Instance->UnicastPort == NULL) {
+ NetFreePool (Instance);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Install the MTFTP protocol onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ &Instance->Mtftp4,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Udp4 protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ MtftpSb->ConnectUdp->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiMtftp4ProtocolGuid,
+ &Instance->Mtftp4,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ //
+ // Add it to the parent's child list.
+ //
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ NetListInsertTail (&MtftpSb->Children, &Instance->Link);
+ MtftpSb->ChildrenNum++;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ON_ERROR:
+
+ if (EFI_ERROR (Status)) {
+ UdpIoFreePort (Instance->UnicastPort);
+ NetFreePool (Instance);
+ }
+
+ return Status;
+}
+
+
+/**
+ Destory one of the service binding's child.
+
+ @param This The service binding instance
+ @param ChildHandle The child handle to destory
+
+ @retval EFI_INVALID_PARAMETER The parameter is invaid.
+ @retval EFI_UNSUPPORTED The child may have already been destoried.
+ @retval EFI_SUCCESS The child is destoried and removed from the
+ parent's child list.
+
+**/
+EFI_STATUS
+Mtftp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ (VOID **) &Mtftp4,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (Mtftp4);
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != MtftpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Instance->Indestory) {
+ return EFI_SUCCESS;
+ }
+
+ Instance->Indestory = TRUE;
+
+ //
+ // Close the Udp4 protocol.
+ //
+ gBS->CloseProtocol (
+ MtftpSb->ConnectUdp->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the MTFTP4 protocol first to enable a top down destruction.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ Mtftp4
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->Indestory = FALSE;
+ return Status;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ Mtftp4CleanOperation (Instance, EFI_DEVICE_ERROR);
+ UdpIoFreePort (Instance->UnicastPort);
+
+ NetListRemoveEntry (&Instance->Link);
+ MtftpSb->ChildrenNum--;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ NetFreePool (Instance);
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h
new file mode 100644
index 0000000000..a9b7ac7121
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h
@@ -0,0 +1,69 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Driver.h
+
+Abstract:
+
+
+**/
+
+#ifndef __EFI_MTFTP4_DRIVER_H__
+#define __EFI_MTFTP4_DRIVER_H__
+
+#include <PiDxe.h>
+
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/NetLib.h>
+#include <Library/UefiLib.h>
+
+
+EFI_STATUS
+Mtftp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+Mtftp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+Mtftp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+EFI_STATUS
+Mtftp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+EFI_STATUS
+Mtftp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+extern EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName;
+extern EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding;
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf
new file mode 100644
index 0000000000..d4198c5140
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf
@@ -0,0 +1,68 @@
+#/** @file
+# Component name for module Mtftp4
+#
+# Copyright (c) 2007, Intel Corporation.
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Mtftp4Dxe
+ FILE_GUID = DC3641B8-2FA8-4ed3-BC1F-F9962A03454B
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = Mtftp4DriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ Mtftp4Option.c
+ Mtftp4Rrq.c
+ Mtftp4Impl.h
+ ComponentName.c
+ Mtftp4Support.c
+ Mtftp4Impl.c
+ Mtftp4Option.h
+ Mtftp4Support.h
+ Mtftp4Driver.h
+ Mtftp4Driver.c
+ Mtftp4Wrq.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ UdpIoLib
+
+
+[Protocols]
+ gEfiMtftp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiMtftp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa
new file mode 100644
index 0000000000..c7b8360ac3
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa
@@ -0,0 +1,79 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>Mtftp4Dxe</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>DC3641B8-2FA8-4ed3-BC1F-F9962A03454B</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Mtftp4</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>Mtftp4Dxe</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>Mtftp4Wrq.c</Filename>
+ <Filename>Mtftp4Driver.c</Filename>
+ <Filename>Mtftp4Driver.h</Filename>
+ <Filename>Mtftp4Support.h</Filename>
+ <Filename>Mtftp4Option.h</Filename>
+ <Filename>Mtftp4Impl.c</Filename>
+ <Filename>Mtftp4Support.c</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>Mtftp4Impl.h</Filename>
+ <Filename>Mtftp4Rrq.c</Filename>
+ <Filename>Mtftp4Option.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUdp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUdp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiMtftp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiMtftp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>Mtftp4DriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c
new file mode 100644
index 0000000000..5bfd2538ef
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c
@@ -0,0 +1,892 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Impl.c
+
+Abstract:
+
+ Interface routine for Mtftp4
+
+
+**/
+
+#include "Mtftp4Impl.h"
+
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4ReadFile (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ );
+
+
+/**
+ Get the current operation parameter for the MTFTP session
+
+ @param This The MTFTP protocol instance
+ @param ModeData The MTFTP mode data
+
+ @retval EFI_INVALID_PARAMETER This or ModeData is NULL
+ @retval EFI_SUCCESS The operation parameter is saved in ModeData
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4GetModeData (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ OUT EFI_MTFTP4_MODE_DATA *ModeData
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ModeData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+ ModeData->ConfigData = Instance->Config;
+ ModeData->SupportedOptionCount = MTFTP4_SUPPORTED_OPTIONS;
+ ModeData->SupportedOptoins = mMtftp4SupportedOptions;
+ ModeData->UnsupportedOptionCount = 0;
+ ModeData->UnsupportedOptoins = NULL;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Clean up the MTFTP session to get ready for new operation.
+
+ @param Instance The MTFTP session to clean up
+ @param Result The result to return to the caller who initiated
+ the operation.
+
+ @return None
+
+**/
+VOID
+Mtftp4CleanOperation (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_STATUS Result
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ MTFTP4_BLOCK_RANGE *Block;
+ EFI_MTFTP4_TOKEN *Token;
+
+ //
+ // Free various resources.
+ //
+ Token = Instance->Token;
+
+ if (Token != NULL) {
+ Token->Status = Result;
+
+ if (Token->Event != NULL) {
+ gBS->SignalEvent (Token->Event);
+ }
+
+ Instance->Token = NULL;
+ }
+
+ ASSERT (Instance->UnicastPort != NULL);
+ UdpIoCleanPort (Instance->UnicastPort);
+
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ Instance->LastPacket = NULL;
+ }
+
+ if (Instance->McastUdpPort != NULL) {
+ UdpIoFreePort (Instance->McastUdpPort);
+ Instance->McastUdpPort = NULL;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->Blocks) {
+ Block = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
+ NetListRemoveEntry (Entry);
+ NetFreePool (Block);
+ }
+
+ NetZeroMem (&Instance->RequestOption, sizeof (MTFTP4_OPTION));
+
+ Instance->Operation = 0;
+
+ Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;
+ Instance->LastBlock = 0;
+ Instance->ServerIp = 0;
+ Instance->ListeningPort = 0;
+ Instance->ConnectedPort = 0;
+ Instance->Gateway = 0;
+ Instance->PacketToLive = 0;
+ Instance->MaxRetry = 0;
+ Instance->CurRetry = 0;
+ Instance->Timeout = 0;
+ Instance->McastIp = 0;
+ Instance->McastPort = 0;
+ Instance->Master = TRUE;
+}
+
+
+/**
+ Configure the MTFTP session for new operation or reset the current
+ operation if ConfigData is NULL.
+
+ @param This The MTFTP session to configure
+ @param ConfigData The configure parameters
+
+ @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.
+ @retval EFI_ACCESS_DENIED There is pending operation
+ @retval EFI_SUCCESS The instance is configured for operation.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4Configure (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_CONFIG_DATA *ConfigData
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_TPL OldTpl;
+ IP4_ADDR Ip;
+ IP4_ADDR Netmask;
+ IP4_ADDR Gateway;
+ IP4_ADDR ServerIp;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ if (ConfigData == NULL) {
+ //
+ // Reset the operation if ConfigData is NULL
+ //
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ Mtftp4CleanOperation (Instance, EFI_ABORTED);
+ NetZeroMem (&Instance->Config, sizeof (EFI_MTFTP4_CONFIG_DATA));
+ Instance->State = MTFTP4_STATE_UNCONFIGED;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ } else {
+ //
+ // Configure the parameters for new operation.
+ //
+ NetCopyMem (&Ip, &ConfigData->StationIp, sizeof (IP4_ADDR));
+ NetCopyMem (&Netmask, &ConfigData->SubnetMask, sizeof (IP4_ADDR));
+ NetCopyMem (&Gateway, &ConfigData->GatewayIp, sizeof (IP4_ADDR));
+ NetCopyMem (&ServerIp, &ConfigData->ServerIp, sizeof (IP4_ADDR));
+
+ Ip = NTOHL (Ip);
+ Netmask = NTOHL (Netmask);
+ Gateway = NTOHL (Gateway);
+ ServerIp = NTOHL (ServerIp);
+
+ if (!Ip4IsUnicast (ServerIp, 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!ConfigData->UseDefaultSetting &&
+ ((!IP4_IS_VALID_NETMASK (Netmask) || !Ip4IsUnicast (Ip, Netmask)))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Gateway != 0) &&
+ (!IP4_NET_EQUAL (Gateway, Ip, Netmask) || !Ip4IsUnicast (Gateway, Netmask))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if ((Instance->State == MTFTP4_STATE_CONFIGED) && (Instance->Operation != 0)) {
+ NET_RESTORE_TPL (OldTpl);
+ return EFI_ACCESS_DENIED;
+ }
+
+ Instance->Config = *ConfigData;
+ Instance->State = MTFTP4_STATE_CONFIGED;
+
+ NET_RESTORE_TPL (OldTpl);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Check packet for GetInfo. GetInfo is implemented with EfiMtftp4ReadFile.
+ It use Mtftp4GetInfoCheckPacket to inspect the first packet from server,
+ then abort the session.
+
+ @param This The MTFTP4 protocol instance
+ @param Token The user's token
+ @param PacketLen The length of the packet
+ @param Packet The received packet.
+
+ @retval EFI_ABORTED Abort the ReadFile operation and return.
+
+**/
+STATIC
+EFI_STATUS
+Mtftp4GetInfoCheckPacket (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP4_PACKET *Packet
+ )
+{
+ MTFTP4_GETINFO_STATE *State;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+
+ State = (MTFTP4_GETINFO_STATE *) Token->Context;
+ OpCode = NTOHS (Packet->OpCode);
+
+ //
+ // Set the GetInfo's return status according to the OpCode.
+ //
+ switch (OpCode) {
+ case EFI_MTFTP4_OPCODE_ERROR:
+ State->Status = EFI_TFTP_ERROR;
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ State->Status = EFI_SUCCESS;
+ break;
+
+ default:
+ State->Status = EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // Allocate buffer then copy the packet over. Use gBS->AllocatePool
+ // in case NetAllocatePool will implements something tricky.
+ //
+ Status = gBS->AllocatePool (EfiBootServicesData, PacketLen, (VOID **) State->Packet);
+
+ if (EFI_ERROR (Status)) {
+ State->Status = EFI_OUT_OF_RESOURCES;
+ return EFI_ABORTED;
+ }
+
+ *(State->PacketLen) = PacketLen;
+ NetCopyMem (*(State->Packet), Packet, PacketLen);
+
+ return EFI_ABORTED;
+}
+
+
+/**
+ Get the information of the download from the server. It is implemented
+ with EfiMtftp4ReadFile: build a token, then pass it to EfiMtftp4ReadFile.
+ In its check packet callback abort the opertions.
+
+ @param This The MTFTP protocol instance
+ @param OverrideData The MTFTP override data
+ @param Filename The file to get information
+ @param ModeStr The mode to use
+ @param OptionCount The number of options to append
+ @param OptionList The options to append
+ @param PacketLength The variable to receive the packet length
+ @param Packet The variable to receive the packet.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invaid
+ @retval EFI_SUCCESS The information is got
+ @retval Others Failed to get the information.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4GetInfo (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_OVERRIDE_DATA *OverrideData, OPTIONAL
+ IN UINT8 *Filename,
+ IN UINT8 *ModeStr, OPTIONAL
+ IN UINT8 OptionCount,
+ IN EFI_MTFTP4_OPTION *OptionList,
+ OUT UINT32 *PacketLength,
+ OUT EFI_MTFTP4_PACKET **Packet OPTIONAL
+ )
+{
+ EFI_MTFTP4_TOKEN Token;
+ MTFTP4_GETINFO_STATE State;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (Filename == NULL) || (PacketLength == NULL) ||
+ (OptionCount && (OptionList == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet != NULL) {
+ *Packet = NULL;
+ }
+
+ *PacketLength = 0;
+ State.Packet = Packet;
+ State.PacketLen = PacketLength;
+ State.Status = EFI_SUCCESS;
+
+ //
+ // Fill in the Token to issue an synchronous ReadFile operation
+ //
+ Token.Status = EFI_SUCCESS;
+ Token.Event = NULL;
+ Token.OverrideData = OverrideData;
+ Token.Filename = Filename;
+ Token.ModeStr = ModeStr;
+ Token.OptionCount = OptionCount;
+ Token.OptionList = OptionList;
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ Token.Context = &State;
+ Token.CheckPacket = Mtftp4GetInfoCheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = EfiMtftp4ReadFile (This, &Token);
+
+ if (EFI_ABORTED == Status) {
+ return State.Status;
+ }
+
+ return Status;
+}
+
+
+/**
+ Parse the packet into an array of options. The OptionList is allocated
+ by this function, and caller should free it when used.
+
+ @param This The MTFTP protocol instance
+ @param PacketLen The length of the packet
+ @param Packet The packet to parse
+ @param OptionCount The size of the OptionList array allocated.
+ @param OptionList The allocated option array to save the option
+ addresses.
+
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_NOT_FOUND There is no valid option in the packet
+ @retval EFI_SUCCESS The packet is parsed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4ParseOptions (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN UINT32 PacketLen,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (PacketLen < MTFTP4_OPCODE_LEN) ||
+ (Packet == NULL) || (OptionCount == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Mtftp4ExtractOptions (Packet, PacketLen, OptionCount, OptionList);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (*OptionCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Check whether the override data is valid. It will first
+ validate whether the server is a valid unicast. If a gateway
+ is provided in the Override, it also check that it is a
+ unicast on the connected network.
+
+ @param Instance The MTFTP instance
+ @param Override The override data to validate.
+
+ @return TRUE if the override data is valid, otherwise FALSE.
+
+**/
+STATIC
+BOOLEAN
+Mtftp4OverrideValid (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_OVERRIDE_DATA *Override
+ )
+{
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ IP4_ADDR Ip;
+ IP4_ADDR Netmask;
+ IP4_ADDR Gateway;
+
+ NetCopyMem (&Ip, &Override->ServerIp, sizeof (IP4_ADDR));
+ if (!Ip4IsUnicast (NTOHL (Ip), 0)) {
+ return FALSE;
+ }
+
+ Config = &Instance->Config;
+
+ NetCopyMem (&Gateway, &Override->GatewayIp, sizeof (IP4_ADDR));
+ Gateway = NTOHL (Gateway);
+
+ if (!Config->UseDefaultSetting && (Gateway != 0)) {
+ NetCopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR));
+ NetCopyMem (&Ip, &Config->StationIp, sizeof (IP4_ADDR));
+
+ Netmask = NTOHL (Netmask);
+ Ip = NTOHL (Ip);
+
+ if (!Ip4IsUnicast (Gateway, Netmask) || !IP4_NET_EQUAL (Gateway, Ip, Netmask)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Poll the UDP to get the IP4 default address, which may be retrieved
+ by DHCP. The default time out value is 5 seconds. If IP has retrieved
+ the default address, the UDP is reconfigured.
+
+ @param Instance The Mtftp instance
+ @param UdpPort The UDP port to poll
+ @param UdpCfgData The UDP configure data to reconfigure the UDP
+ port.
+
+ @return TRUE if the default address is retrieved and UDP is reconfigured.
+ @return Otherwise FALSE.
+
+**/
+BOOLEAN
+Mtftp4GetMapping (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UDP_IO_PORT *UdpPort,
+ IN EFI_UDP4_CONFIG_DATA *UdpCfgData
+ )
+{
+ MTFTP4_SERVICE *Service;
+ EFI_IP4_MODE_DATA Ip4Mode;
+ EFI_UDP4_PROTOCOL *Udp;
+ EFI_STATUS Status;
+
+ ASSERT (Instance->Config.UseDefaultSetting);
+
+ Service = Instance->Service;
+ Udp = UdpPort->Udp;
+
+ Status = gBS->SetTimer (
+ Service->TimerToGetMap,
+ TimerRelative,
+ MTFTP4_TIME_TO_GETMAP * TICKS_PER_SECOND
+ );
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ while (!EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) {
+ Udp->Poll (Udp);
+
+ if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip4Mode, NULL, NULL)) &&
+ Ip4Mode.IsConfigured) {
+
+ Udp->Configure (Udp, NULL);
+ return (BOOLEAN) (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS);
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Configure the UDP port for unicast receiving.
+
+ @param UdpIo The UDP port
+ @param Instance The MTFTP session
+
+ @retval EFI_SUCCESS The UDP port is successfully configured for the
+ session to unicast receive.
+
+**/
+STATIC
+EFI_STATUS
+Mtftp4ConfigUnicastPort (
+ IN UDP_IO_PORT *UdpIo,
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_UDP4_CONFIG_DATA UdpConfig;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ Config = &Instance->Config;
+
+ UdpConfig.AcceptBroadcast = FALSE;
+ UdpConfig.AcceptPromiscuous = FALSE;
+ UdpConfig.AcceptAnyPort = FALSE;
+ UdpConfig.AllowDuplicatePort = FALSE;
+ UdpConfig.TypeOfService = 0;
+ UdpConfig.TimeToLive = 64;
+ UdpConfig.DoNotFragment = FALSE;
+ UdpConfig.ReceiveTimeout = 0;
+ UdpConfig.TransmitTimeout = 0;
+ UdpConfig.UseDefaultAddress = Config->UseDefaultSetting;
+ UdpConfig.StationAddress = Config->StationIp;
+ UdpConfig.SubnetMask = Config->SubnetMask;
+ UdpConfig.StationPort = 0;
+ UdpConfig.RemotePort = 0;
+
+ Ip = HTONL (Instance->ServerIp);
+ NetCopyMem (&UdpConfig.RemoteAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Status = UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfig);
+
+ if ((Status == EFI_NO_MAPPING) && Mtftp4GetMapping (Instance, UdpIo, &UdpConfig)) {
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+/**
+ Start the MTFTP session to do the operation, such as read file,
+ write file, and read directory.
+
+ @param This The MTFTP session
+ @param Token The token than encapsues the user's request.
+ @param Operation The operation to do
+
+ @retval EFI_INVALID_PARAMETER Some of the parameters are invalid.
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.
+ @retval EFI_ALREADY_STARTED There is pending operation for the session.
+ @retval EFI_SUCCESS The operation is successfully started.
+
+**/
+STATIC
+EFI_STATUS
+Mtftp4Start (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token,
+ IN UINT16 Operation
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_OVERRIDE_DATA *Override;
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Validate the parameters
+ //
+ if ((This == NULL) || (Token == NULL) || (Token->Filename == NULL) ||
+ ((Token->OptionCount != 0) && (Token->OptionList == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // User must provide at least one method to collect the data for download.
+ //
+ if (((Operation == EFI_MTFTP4_OPCODE_RRQ) || (Operation == EFI_MTFTP4_OPCODE_DIR)) &&
+ ((Token->Buffer == NULL) && (Token->CheckPacket == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // User must provide at least one method to provide the data for upload.
+ //
+ if ((Operation == EFI_MTFTP4_OPCODE_WRQ) &&
+ ((Token->Buffer == NULL) && (Token->PacketNeeded == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ Status = EFI_SUCCESS;
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (Instance->State != MTFTP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ }
+
+ if (Instance->Operation != 0) {
+ Status = EFI_ACCESS_DENIED;
+ }
+
+ if (EFI_ERROR (Status)) {
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // Set the Operation now to prevent the application start other
+ // operations.
+ //
+ Instance->Operation = Operation;
+ Override = Token->OverrideData;
+
+ if ((Override != NULL) && !Mtftp4OverrideValid (Instance, Override)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_ERROR;
+ }
+
+ if (Token->OptionCount != 0) {
+ Status = Mtftp4ParseOption (
+ Token->OptionList,
+ Token->OptionCount,
+ TRUE,
+ &Instance->RequestOption
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Set the operation parameters from the configuration or override data.
+ //
+ Config = &Instance->Config;
+ Instance->Token = Token;
+ Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;
+
+ NetCopyMem (&Instance->ServerIp, &Config->ServerIp, sizeof (IP4_ADDR));
+ Instance->ServerIp = NTOHL (Instance->ServerIp);
+
+ Instance->ListeningPort = Config->InitialServerPort;
+ Instance->ConnectedPort = 0;
+
+ NetCopyMem (&Instance->Gateway, &Config->GatewayIp, sizeof (IP4_ADDR));
+ Instance->Gateway = NTOHL (Instance->Gateway);
+
+ Instance->MaxRetry = Config->TryCount;
+ Instance->Timeout = Config->TimeoutValue;
+ Instance->Master = TRUE;
+
+ if (Override != NULL) {
+ NetCopyMem (&Instance->ServerIp, &Override->ServerIp, sizeof (IP4_ADDR));
+ NetCopyMem (&Instance->Gateway, &Override->GatewayIp, sizeof (IP4_ADDR));
+
+ Instance->ServerIp = NTOHL (Instance->ServerIp);
+ Instance->Gateway = NTOHL (Instance->Gateway);
+
+ Instance->ListeningPort = Override->ServerPort;
+ Instance->MaxRetry = Override->TryCount;
+ Instance->Timeout = Override->TimeoutValue;
+ }
+
+ if (Instance->ListeningPort == 0) {
+ Instance->ListeningPort = MTFTP4_DEFAULT_SERVER_PORT;
+ }
+
+ if (Instance->MaxRetry == 0) {
+ Instance->MaxRetry = MTFTP4_DEFAULT_RETRY;
+ }
+
+ if (Instance->Timeout == 0) {
+ Instance->Timeout = MTFTP4_DEFAULT_TIMEOUT;
+ }
+
+ //
+ // Config the unicast UDP child to send initial request
+ //
+ Status = Mtftp4ConfigUnicastPort (Instance->UnicastPort, Instance);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Build and send an initial requests
+ //
+ if (Operation == EFI_MTFTP4_OPCODE_WRQ) {
+ Status = Mtftp4WrqStart (Instance, Operation);
+ } else {
+ Status = Mtftp4RrqStart (Instance, Operation);
+ }
+
+ NET_RESTORE_TPL (OldTpl);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ //
+ // Return immediately for asynchronous operation or poll the
+ // instance for synchronous operation.
+ //
+ Token->Status = EFI_NOT_READY;
+
+ if (Token->Event != NULL) {
+ return EFI_SUCCESS;
+ }
+
+ while (Token->Status == EFI_NOT_READY) {
+ This->Poll (This);
+ }
+
+ return Token->Status;
+
+ON_ERROR:
+ Mtftp4CleanOperation (Instance, Status);
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Read a file from the server.
+
+ @param This The Mtftp protocol instance.
+ @param Token The user's request wrap token.
+
+ @retval EFI_SUCCESS The ReadFile has finished, the file has been
+ downloaded if it is synchronous operation,
+ otherwise it has been initated.
+ @retval Others Some error happened.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4ReadFile (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_RRQ);
+}
+
+
+/**
+ Upload a file to the server.
+
+ @param This The MTFTP protocol session
+ @param Token The user's request wrap token.
+
+ @retval EFI_SUCCESS The WriteFile has finished, the file has been
+ uploaded if it is synchronous operation, otherwise
+ it has been initated.
+ @retval Others Some error happened.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4WriteFile (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_WRQ);
+}
+
+
+/**
+ Read a directory from the server. The only difference
+ between ReadFile and ReadDirectory is the opcode used.
+
+ @param This The MTFTP protocol session
+ @param Token The user's request wrap token.
+
+ @retval EFI_SUCCESS The ReadDirectory has finished, the directory has
+ been downloaded as a file if it is synchronous
+ operation, otherwise it has been initated.
+ @retval Others Some error happened.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4ReadDirectory (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_DIR);
+}
+
+
+/**
+ Poll the network stack to accelerate the packet process.
+
+ @param This The MTFTP protocol instance.
+
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.
+ @retval EFI_DEVICE_ERROR The MTFTP session has been destoried.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EfiMtftp4Poll (
+ IN EFI_MTFTP4_PROTOCOL *This
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_UDP4_PROTOCOL *Udp;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ if (Instance->State == MTFTP4_STATE_UNCONFIGED) {
+ return EFI_NOT_STARTED;
+ } else if (Instance->State == MTFTP4_STATE_DESTORY) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Udp = Instance->UnicastPort->Udp;
+ return Udp->Poll (Udp);
+}
+
+EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate = {
+ EfiMtftp4GetModeData,
+ EfiMtftp4Configure,
+ EfiMtftp4GetInfo,
+ EfiMtftp4ParseOptions,
+ EfiMtftp4ReadFile,
+ EfiMtftp4WriteFile,
+ EfiMtftp4ReadDirectory,
+ EfiMtftp4Poll
+};
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h
new file mode 100644
index 0000000000..24afe6b7cf
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h
@@ -0,0 +1,176 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Impl.h
+
+Abstract:
+
+ Mtftp4 Implementation, it supports the following RFCs:
+ RFC1350 - THE TFTP PROTOCOL (REVISION 2)
+ RFC2090 - TFTP Multicast Option
+ RFC2347 - TFTP Option Extension
+ RFC2348 - TFTP Blocksize Option
+ RFC2349 - TFTP Timeout Interval and Transfer Size Options
+
+
+**/
+
+#ifndef __EFI_MTFTP4_IMPL_H__
+#define __EFI_MTFTP4_IMPL_H__
+
+#include <PiDxe.h>
+
+#include <Protocol/Udp4.h>
+#include <Protocol/Mtftp4.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+
+typedef struct _MTFTP4_SERVICE MTFTP4_SERVICE;
+typedef struct _MTFTP4_PROTOCOL MTFTP4_PROTOCOL;
+
+#include "Mtftp4Driver.h"
+#include "Mtftp4Option.h"
+#include "Mtftp4Support.h"
+
+enum {
+ MTFTP4_SERVICE_SIGNATURE = EFI_SIGNATURE_32 ('T', 'F', 'T', 'P'),
+ MTFTP4_PROTOCOL_SIGNATURE = EFI_SIGNATURE_32 ('t', 'f', 't', 'p'),
+
+ MTFTP4_DEFAULT_SERVER_PORT = 69,
+ MTFTP4_DEFAULT_TIMEOUT = 3,
+ MTFTP4_DEFAULT_RETRY = 5,
+ MTFTP4_DEFAULT_BLKSIZE = 512,
+ MTFTP4_TIME_TO_GETMAP = 5,
+
+ MTFTP4_STATE_UNCONFIGED = 0,
+ MTFTP4_STATE_CONFIGED,
+ MTFTP4_STATE_DESTORY,
+};
+
+typedef struct _MTFTP4_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ BOOLEAN InDestory;
+
+ UINT16 ChildrenNum;
+ NET_LIST_ENTRY Children;
+
+ EFI_EVENT Timer; // Ticking timer for all the MTFTP clients
+ EFI_EVENT TimerToGetMap;
+
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ //
+ // This UDP child is used to keep the connection between the UDP
+ // and MTFTP, so MTFTP will be notified when UDP is uninstalled.
+ //
+ UDP_IO_PORT *ConnectUdp;
+};
+
+typedef struct _MTFTP4_PROTOCOL {
+ UINT32 Signature;
+ NET_LIST_ENTRY Link;
+ EFI_MTFTP4_PROTOCOL Mtftp4;
+
+ INTN State;
+ BOOLEAN Indestory;
+
+ MTFTP4_SERVICE *Service;
+ EFI_HANDLE Handle;
+
+ EFI_MTFTP4_CONFIG_DATA Config;
+
+ //
+ // Operation parameters: token and requested options.
+ //
+ EFI_MTFTP4_TOKEN *Token;
+ MTFTP4_OPTION RequestOption;
+ UINT16 Operation;
+
+ //
+ // Blocks is a list of MTFTP4_BLOCK_RANGE which contains
+ // holes in the file
+ //
+ UINT16 BlkSize;
+ UINT16 LastBlock;
+ NET_LIST_ENTRY Blocks;
+
+ //
+ // The server's communication end point: IP and two ports. one for
+ // initial request, one for its selected port.
+ //
+ IP4_ADDR ServerIp;
+ UINT16 ListeningPort;
+ UINT16 ConnectedPort;
+ IP4_ADDR Gateway;
+ UDP_IO_PORT *UnicastPort;
+
+ //
+ // Timeout and retransmit status
+ //
+ NET_BUF *LastPacket;
+ UINT32 PacketToLive;
+ UINT32 CurRetry;
+ UINT32 MaxRetry;
+ UINT32 Timeout;
+
+ //
+ // Parameter used by RRQ's multicast download.
+ //
+ IP4_ADDR McastIp;
+ UINT16 McastPort;
+ BOOLEAN Master;
+ UDP_IO_PORT *McastUdpPort;
+};
+
+typedef struct {
+ EFI_MTFTP4_PACKET **Packet;
+ UINT32 *PacketLen;
+ EFI_STATUS Status;
+} MTFTP4_GETINFO_STATE;
+
+VOID
+Mtftp4CleanOperation (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_STATUS Result
+ );
+
+EFI_STATUS
+Mtftp4WrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ );
+
+EFI_STATUS
+Mtftp4RrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ );
+
+#define MTFTP4_SERVICE_FROM_THIS(a) \
+ CR (a, MTFTP4_SERVICE, ServiceBinding, MTFTP4_SERVICE_SIGNATURE)
+
+#define MTFTP4_PROTOCOL_FROM_THIS(a) \
+ CR (a, MTFTP4_PROTOCOL, Mtftp4, MTFTP4_PROTOCOL_SIGNATURE)
+
+extern EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate;
+#endif
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c
new file mode 100644
index 0000000000..b85784a926
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c
@@ -0,0 +1,542 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Option.c
+
+Abstract:
+ routines to process MTFTP4 options
+
+
+**/
+
+#include "Mtftp4Impl.h"
+
+UINT8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = {
+ "blksize",
+ "timeout",
+ "tsize",
+ "multicast"
+};
+
+
+/**
+ Go through the packet to fill the Options array with the start
+ addresses of each MTFTP option name/value pair.
+
+ @param Packet The packet to check
+ @param PacketLen The packet's length
+ @param Count The size of the Options on input. The actual
+ options on output
+ @param Options The option array to fill in
+
+ @retval EFI_INVALID_PARAMETER The packet is mal-formated
+ @retval EFI_BUFFER_TOO_SMALL The Options array is too small
+ @retval EFI_SUCCESS The packet has been parsed into the Options array.
+
+**/
+STATIC
+EFI_STATUS
+Mtftp4FillOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *Count,
+ OUT EFI_MTFTP4_OPTION *Options OPTIONAL
+ )
+{
+ UINT8 *Cur;
+ UINT8 *Last;
+ UINT8 Num;
+ UINT8 *Name;
+ UINT8 *Value;
+
+ Num = 0;
+ Cur = (UINT8 *) Packet + MTFTP4_OPCODE_LEN;
+ Last = (UINT8 *) Packet + PacketLen - 1;
+
+ //
+ // process option name and value pairs. The last byte is always zero
+ //
+ while (Cur < Last) {
+ Name = Cur;
+
+ while (*Cur != 0) {
+ Cur++;
+ }
+
+ if (Cur == Last) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value = ++Cur;
+
+ while (*Cur != 0) {
+ Cur++;
+ }
+
+ Num++;
+
+ if ((Options != NULL) && (Num <= *Count)) {
+ Options[Num - 1].OptionStr = Name;
+ Options[Num - 1].ValueStr = Value;
+ }
+
+ Cur++;
+ }
+
+ if ((*Count < Num) || (Options == NULL)) {
+ *Count = Num;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *Count = Num;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Allocate and fill in a array of Mtftp options from the Packet. It
+ first calls Mtftp4FillOption to get the option number, then allocate
+ the array, at last, call Mtftp4FillOption again to save the options.
+
+ @param Packet The packet to parse
+ @param PacketLen The length of the packet
+ @param OptionCount The number of options in the packet
+ @param OptionList The point to get the option array.
+
+ @retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a
+ well-formated OACK packet.
+ @retval EFI_SUCCESS The option array is build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array
+
+**/
+EFI_STATUS
+Mtftp4ExtractOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ *OptionCount = 0;
+
+ if (OptionList != NULL) {
+ *OptionList = NULL;
+ }
+
+ if (NTOHS (Packet->OpCode) != EFI_MTFTP4_OPCODE_OACK) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (PacketLen == MTFTP4_OPCODE_LEN) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // The last byte must be zero to terminate the options
+ //
+ if (*((UINT8 *) Packet + PacketLen - 1) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Get the number of options
+ //
+ Status = Mtftp4FillOptions (Packet, PacketLen, OptionCount, NULL);
+
+ if ((Status == EFI_SUCCESS) || (Status != EFI_BUFFER_TOO_SMALL)) {
+ return Status;
+ }
+
+ //
+ // Allocate memory for the options, then call Mtftp4FillOptions to
+ // fill it if caller want that.
+ //
+ if (OptionList == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ *OptionList = NetAllocatePool (*OptionCount * sizeof (EFI_MTFTP4_OPTION));
+
+ if (*OptionList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Mtftp4FillOptions (Packet, PacketLen, OptionCount, *OptionList);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Check whether two ascii strings are equel, ignore the case.
+
+ @param Str1 The first ascii string
+ @param Str2 The second ascii string
+
+ @retval TRUE Two strings are equal when case is ignored.
+ @retval FALSE Two string are not equal.
+
+**/
+BOOLEAN
+NetStringEqualNoCase (
+ IN UINT8 *Str1,
+ IN UINT8 *Str2
+ )
+{
+ UINT8 Ch1;
+ UINT8 Ch2;
+
+ ASSERT ((Str1 != NULL) && (Str2 != NULL));
+
+ for (; (*Str1 != '\0') && (*Str2 != '\0'); Str1++, Str2++) {
+ Ch1 = *Str1;
+ Ch2 = *Str2;
+
+ //
+ // Convert them to lower case then compare two
+ //
+ if (('A' <= Ch1) && (Ch1 <= 'Z')) {
+ Ch1 += 'a' - 'A';
+ }
+
+ if (('A' <= Ch2) && (Ch2 <= 'Z')) {
+ Ch2 += 'a' - 'A';
+ }
+
+ if (Ch1 != Ch2) {
+ return FALSE;
+ }
+ }
+
+ return (BOOLEAN) (*Str1 == *Str2);
+}
+
+
+/**
+ Convert a string to a UINT32 number.
+
+ @param Str The string to convert from
+
+ @return The number get from the string
+
+**/
+UINT32
+NetStringToU32 (
+ IN UINT8 *Str
+ )
+{
+ UINT32 Num;
+
+ ASSERT (Str != NULL);
+
+ Num = 0;
+
+ for (; NET_IS_DIGIT (*Str); Str++) {
+ Num = Num * 10 + (*Str - '0');
+ }
+
+ return Num;
+}
+
+
+/**
+ Convert a string of the format "192.168.0.1" to an IP address.
+
+ @param Str The string representation of IP
+ @param Ip The varible to get IP.
+
+ @retval EFI_INVALID_PARAMETER The IP string is invalid.
+ @retval EFI_SUCCESS The IP is parsed into the Ip
+
+**/
+STATIC
+EFI_STATUS
+NetStringToIp (
+ IN UINT8 *Str,
+ OUT IP4_ADDR *Ip
+ )
+{
+ UINT32 Byte;
+ UINT32 Addr;
+ UINTN Index;
+
+ *Ip = 0;
+ Addr = 0;
+
+ for (Index = 0; Index < 4; Index++) {
+ if (!NET_IS_DIGIT (*Str)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Byte = NetStringToU32 (Str);
+
+ if (Byte > 255) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Addr = (Addr << 8) | Byte;
+
+ //
+ // Skip all the digitals and check whether the sepeator is the dot
+ //
+ while (NET_IS_DIGIT (*Str)) {
+ Str++;
+ }
+
+ if ((Index < 3) && (*Str != '.')) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Str++;
+ }
+
+ *Ip = Addr;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the MTFTP multicast option.
+
+ @param Value The Mtftp multicast value string
+ @param Option The option to save the info into.
+
+ @retval EFI_INVALID_PARAMETER The multicast value string is invalid.
+ @retval EFI_SUCCESS The multicast value is parsed into the Option
+
+**/
+STATIC
+EFI_STATUS
+Mtftp4ExtractMcast (
+ IN UINT8 *Value,
+ IN MTFTP4_OPTION *Option
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Num;
+
+ //
+ // The multicast option is formated like "204.0.0.1,1857,1"
+ // The server can also omit the ip and port, use ",,1"
+ //
+ if (*Value == ',') {
+ Option->McastIp = 0;
+ } else {
+ Status = NetStringToIp (Value, &Option->McastIp);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ while (*Value && (*Value != ',')) {
+ Value++;
+ }
+ }
+
+ if (*Value != ',') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value++;
+
+ //
+ // Convert the port setting. the server can send us a port number or
+ // empty string. such as the port in ",,1"
+ //
+ if (*Value == ',') {
+ Option->McastPort = 0;
+ } else {
+ Num = NetStringToU32 (Value);
+
+ if (Num > 65535) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Option->McastPort = (UINT16)Num;
+
+ while (NET_IS_DIGIT (*Value)) {
+ Value++;
+ }
+ }
+
+ if (*Value != ',') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value++;
+
+ //
+ // Check the master/slave setting, 1 for master, 0 for slave.
+ //
+ Num = NetStringToU32 (Value);
+
+ if ((Num != 0) && (Num != 1)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Option->Master = (BOOLEAN)(Num == 1);
+
+ while (NET_IS_DIGIT (*Value)) {
+ Value++;
+ }
+
+ if (*Value != '\0') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the option in Options array to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Options The option array, which contains addresses of each
+ option's name/value string.
+ @param Count The number of options in the Options
+ @param Request Whether this is a request or OACK. The format of
+ multicast is different according to this setting.
+ @param MtftpOption The MTFTP4_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The option is mal-formated
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOption (
+ IN EFI_MTFTP4_OPTION *Options,
+ IN UINT32 Count,
+ IN BOOLEAN Request,
+ OUT MTFTP4_OPTION *MtftpOption
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT32 Value;
+ EFI_MTFTP4_OPTION *This;
+
+ MtftpOption->Exist = 0;
+
+ for (Index = 0; Index < Count; Index++) {
+ This = Options + Index;
+
+ if ((This->OptionStr == NULL) || (This->ValueStr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NetStringEqualNoCase (This->OptionStr, "blksize")) {
+ //
+ // block size option, valid value is between [8, 65464]
+ //
+ Value = NetStringToU32 (This->ValueStr);
+
+ if ((Value < 8) || (Value > 65464)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MtftpOption->BlkSize = (UINT16) Value;
+ MtftpOption->Exist |= MTFTP4_BLKSIZE_EXIST;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, "timeout")) {
+ //
+ // timeout option, valid value is between [1, 255]
+ //
+ Value = NetStringToU32 (This->ValueStr);
+
+ if ((Value < 1) || (Value > 255)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MtftpOption->Timeout = (UINT8) Value;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, "tsize")) {
+ //
+ // tsize option, the biggest transfer supported is 4GB with block size option
+ //
+ MtftpOption->Tsize = NetStringToU32 (This->ValueStr);
+ MtftpOption->Exist |= MTFTP4_TSIZE_EXIST;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, "multicast")) {
+ //
+ // Multicast option, if it is a request, the value must be a zero
+ // length string, otherwise, it is formated like "204.0.0.1,1857,1\0"
+ //
+ if (Request) {
+ if (*(This->ValueStr) != '\0') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else {
+ Status = Mtftp4ExtractMcast (This->ValueStr, MtftpOption);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ MtftpOption->Exist |= MTFTP4_MCAST_EXIST;
+
+ } else if (Request) {
+ //
+ // Ignore the unsupported option if it is a reply, and return
+ // EFI_UNSUPPORTED if it's a request according to the UEFI spec.
+ //
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the options in the OACK packet to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Packet The OACK packet to parse
+ @param PacketLen The length of the packet
+ @param MtftpOption The MTFTP_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The packet option is mal-formated
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOptionOack (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ OUT MTFTP4_OPTION *MtftpOption
+ )
+{
+ EFI_MTFTP4_OPTION *OptionList;
+ EFI_STATUS Status;
+ UINT32 Count;
+
+ MtftpOption->Exist = 0;
+
+ Status = Mtftp4ExtractOptions (Packet, PacketLen, &Count, &OptionList);
+
+ if (EFI_ERROR (Status) || (Count == 0)) {
+ return Status;
+ }
+
+ Status = Mtftp4ParseOption (OptionList, Count, FALSE, MtftpOption);
+
+ NetFreePool (OptionList);
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h
new file mode 100644
index 0000000000..a136304df4
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h
@@ -0,0 +1,73 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Option.h
+
+Abstract:
+
+ Mtftp4 option process routines.
+
+
+**/
+
+#ifndef __EFI_MTFTP4_OPTION_H__
+#define __EFI_MTFTP4_OPTION_H__
+
+enum {
+ MTFTP4_SUPPORTED_OPTIONS = 4,
+ MTFTP4_OPCODE_LEN = 2,
+ MTFTP4_ERRCODE_LEN = 2,
+ MTFTP4_BLKNO_LEN = 2,
+ MTFTP4_DATA_HEAD_LEN = 4,
+
+ MTFTP4_BLKSIZE_EXIST = 0x01,
+ MTFTP4_TIMEOUT_EXIST = 0x02,
+ MTFTP4_TSIZE_EXIST = 0x04,
+ MTFTP4_MCAST_EXIST = 0x08,
+};
+
+typedef struct {
+ UINT16 BlkSize;
+ UINT8 Timeout;
+ UINT32 Tsize;
+ IP4_ADDR McastIp;
+ UINT16 McastPort;
+ BOOLEAN Master;
+ UINT32 Exist;
+} MTFTP4_OPTION;
+
+EFI_STATUS
+Mtftp4ExtractOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ );
+
+EFI_STATUS
+Mtftp4ParseOption (
+ IN EFI_MTFTP4_OPTION *OptionList,
+ IN UINT32 Count,
+ IN BOOLEAN Request,
+ OUT MTFTP4_OPTION *Option
+ );
+
+EFI_STATUS
+Mtftp4ParseOptionOack (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ OUT MTFTP4_OPTION *Option
+ );
+
+extern UINT8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS];
+#endif
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c
new file mode 100644
index 0000000000..665114c7f8
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c
@@ -0,0 +1,735 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Rrq.c
+
+Abstract:
+
+ Routines to process Rrq (download)
+
+
+**/
+
+#include "Mtftp4Impl.h"
+
+VOID
+Mtftp4RrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_POINTS *Points,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+
+/**
+ Start the MTFTP session to download. It will first initialize some
+ of the internal states then build and send a RRQ reqeuest packet, at
+ last, it will start receive for the downloading.
+
+ @param Instance The Mtftp session
+ @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
+ or EFI_MTFTP4_OPCODE_DIR.
+
+ @retval EFI_SUCCESS The mtftp download session is started.
+ @retval Others Failed to start downloading.
+
+**/
+EFI_STATUS
+Mtftp4RrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [1, 0xffff]. For example:
+ // the client sends an RRQ request to the server, the server
+ // transfers the DATA1 block. If option negoitation is ongoing,
+ // the server will send back an OACK, then client will send ACK0.
+ //
+ Status = Mtftp4InitBlockRange (&Instance->Blocks, 1, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp4SendRequest (Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
+}
+
+
+/**
+ Build and send a ACK packet for the download session.
+
+ @param Instance The Mtftp session
+ @param BlkNo The BlkNo to ack.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
+ @retval EFI_SUCCESS The ACK has been sent
+ @retval Others Failed to send the ACK.
+
+**/
+EFI_STATUS
+Mtftp4RrqSendAck (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 BlkNo
+ )
+{
+ EFI_MTFTP4_PACKET *Ack;
+ NET_BUF *Packet;
+
+ Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));
+
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ack = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (
+ Packet,
+ sizeof (EFI_MTFTP4_ACK_HEADER),
+ FALSE
+ );
+
+ Ack->Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
+ Ack->Ack.Block[0] = HTONS (BlkNo);
+
+ return Mtftp4SendPacket (Instance, Packet);
+}
+
+
+/**
+ Deliver the received data block to the user, which can be saved
+ in the user provide buffer or through the CheckPacket callback.
+
+ @param Instance The Mtftp session
+ @param Packet The received data packet
+ @param Len The packet length
+
+ @retval EFI_SUCCESS The data is saved successfully
+ @retval EFI_ABORTED The user tells to abort by return an error through
+ CheckPacket
+ @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is
+ updated to the actual buffer size needed.
+
+**/
+EFI_STATUS
+Mtftp4RrqSaveBlock (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len
+ )
+{
+ EFI_MTFTP4_TOKEN *Token;
+ EFI_STATUS Status;
+ UINT16 Block;
+ UINT64 Start;
+ UINT32 DataLen;
+
+ Token = Instance->Token;
+ Block = NTOHS (Packet->Data.Block);
+ DataLen = Len - MTFTP4_DATA_HEAD_LEN;
+
+ //
+ // This is the last block, save the block no
+ //
+ if (DataLen < Instance->BlkSize) {
+ Instance->LastBlock = Block;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, Block);
+ }
+
+ //
+ // Remove this block number from the file hole. If Mtftp4RemoveBlockNum
+ // returns EFI_NOT_FOUND, the block has been saved, don't save it again.
+ //
+ Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block);
+
+ if (Status == EFI_NOT_FOUND) {
+ return EFI_SUCCESS;
+ } else if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Token->CheckPacket != NULL) {
+ Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16) Len, Packet);
+
+ if (EFI_ERROR (Status)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ "User aborted download"
+ );
+
+ return EFI_ABORTED;
+ }
+ }
+
+ if (Token->Buffer != NULL) {
+ Start = MultU64x32 (Block - 1, Instance->BlkSize);
+
+ if (Start + DataLen <= Token->BufferSize) {
+ NetCopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);
+
+ //
+ // Update the file size when received the last block
+ //
+ if (Instance->LastBlock == Block) {
+ Token->BufferSize = Start + DataLen;
+ }
+
+ } else if (Instance->LastBlock != 0) {
+ //
+ // Don't save the data if the buffer is too small, return
+ // EFI_BUFFER_TOO_SMALL if received the last packet. This
+ // will give a accurate file length.
+ //
+ Token->BufferSize = Start + DataLen;
+
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_DISK_FULL,
+ "User provided memory block is too small"
+ );
+
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Function to process the received data packets. It will save the block
+ then send back an ACK if it is active.
+
+ @param Instance The downloading MTFTP session
+ @param Packet The packet received
+ @param Len The length of the packet
+ @param Multicast Whether this packet is multicast or unicast
+ @param Completed Return whether the download has completed
+
+ @retval EFI_SUCCESS The data packet is successfully processed
+ @retval EFI_ABORTED The download is aborted by the user
+ @retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small
+
+**/
+EFI_STATUS
+Mtftp4RrqHandleData (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ IN BOOLEAN Multicast,
+ OUT BOOLEAN *Completed
+ )
+{
+ EFI_STATUS Status;
+ UINT16 BlockNum;
+ INTN Expected;
+
+ *Completed = FALSE;
+ BlockNum = NTOHS (Packet->Data.Block);
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // If we are active and received an unexpected packet, retransmit
+ // the last ACK then restart receiving. If we are passive, save
+ // the block.
+ //
+ if (Instance->Master && (Expected != BlockNum)) {
+ Mtftp4Retransmit (Instance);
+ return EFI_SUCCESS;
+ }
+
+ Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Reset the passive client's timer whenever it received a
+ // valid data packet.
+ //
+ if (!Instance->Master) {
+ Mtftp4SetTimeout (Instance);
+ }
+
+ //
+ // Check whether we have received all the blocks. Send the ACK if we
+ // are active (unicast client or master client for multicast download).
+ // If we have received all the blocks, send an ACK even if we are passive
+ // to tell the server that we are done.
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Instance->Master || (Expected < 0)) {
+ if (Expected < 0) {
+ //
+ // If we are passive client, then the just received Block maybe
+ // isn't the last block. We need to send an ACK to the last block
+ // to inform the server that we are done. If we are active client,
+ // the Block == Instance->LastBlock.
+ //
+ BlockNum = Instance->LastBlock;
+ *Completed = TRUE;
+
+ } else {
+ BlockNum = (UINT16) (Expected - 1);
+ }
+
+ Mtftp4RrqSendAck (Instance, BlockNum);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Validate whether the options received in the server's OACK packet is valid.
+ The options are valid only if:
+ 1. The server doesn't include options not requested by us
+ 2. The server can only use smaller blksize than that is requested
+ 3. The server can only use the same timeout as requested
+ 4. The server doesn't change its multicast channel.
+
+ @param This The downloading Mtftp session
+ @param Reply The options in the OACK packet
+ @param Request The requested options
+
+ @return TRUE if the options in the OACK is OK, otherwise FALSE.
+
+**/
+BOOLEAN
+Mtftp4RrqOackValid (
+ IN MTFTP4_PROTOCOL *This,
+ IN MTFTP4_OPTION *Reply,
+ IN MTFTP4_OPTION *Request
+ )
+{
+
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((Reply->Exist &~Request->Exist) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size to be used and
+ // return the timeout matches that requested.
+ //
+ if (((Reply->Exist & MTFTP4_BLKSIZE_EXIST) && (Reply->BlkSize > Request->BlkSize)) ||
+ ((Reply->Exist & MTFTP4_TIMEOUT_EXIST) && (Reply->Timeout != Request->Timeout))) {
+ return FALSE;
+ }
+
+ //
+ // The server can send ",,master" to client to change its master
+ // setting. But if it use the specific multicast channel, it can't
+ // change the setting.
+ //
+ if ((Reply->Exist & MTFTP4_MCAST_EXIST) && (This->McastIp != 0)) {
+ if ((Reply->McastIp != 0) && (Reply->McastIp != This->McastIp)) {
+ return FALSE;
+ }
+
+ if ((Reply->McastPort != 0) && (Reply->McastPort != This->McastPort)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Configure a UDP IO port to receive the multicast.
+
+ @param McastIo The UDP IO port to configure
+ @param Context The opaque parameter to the function which is the
+ MTFTP session.
+
+ @retval EFI_SUCCESS The udp child is successfully configured.
+ @retval Others Failed to configure the UDP child.
+
+**/
+STATIC
+EFI_STATUS
+Mtftp4RrqConfigMcastPort (
+ IN UDP_IO_PORT *McastIo,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_UDP4_CONFIG_DATA UdpConfig;
+ EFI_IPv4_ADDRESS Group;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ Config = &Instance->Config;
+
+ UdpConfig.AcceptBroadcast = FALSE;
+ UdpConfig.AcceptPromiscuous = FALSE;
+ UdpConfig.AcceptAnyPort = FALSE;
+ UdpConfig.AllowDuplicatePort = FALSE;
+ UdpConfig.TypeOfService = 0;
+ UdpConfig.TimeToLive = 64;
+ UdpConfig.DoNotFragment = FALSE;
+ UdpConfig.ReceiveTimeout = 0;
+ UdpConfig.TransmitTimeout = 0;
+ UdpConfig.UseDefaultAddress = Config->UseDefaultSetting;
+ UdpConfig.StationAddress = Config->StationIp;
+ UdpConfig.SubnetMask = Config->SubnetMask;
+ UdpConfig.StationPort = Instance->McastPort;
+ UdpConfig.RemotePort = 0;
+
+ Ip = HTONL (Instance->ServerIp);
+ NetCopyMem (&UdpConfig.RemoteAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Status = McastIo->Udp->Configure (McastIo->Udp, &UdpConfig);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // join the multicast group
+ //
+ Ip = HTONL (Instance->McastIp);
+ NetCopyMem (&Group, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ return McastIo->Udp->Groups (McastIo->Udp, TRUE, &Group);
+}
+
+
+/**
+ Function to process the OACK. It will first validate the OACK
+ packet, then update the various negotiated parameters.
+
+ @param Instance The download MTFTP session
+ @param Packet The packet received
+ @param Len The packet length
+ @param Multicast Whether this packet is received as a multicast
+ @param Completed Returns whether the download has completed. NOT
+ used by this function.
+
+ @retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child
+ @retval EFI_TFTP_ERROR Some error happened during the process
+ @retval EFI_SUCCESS The OACK is successfully processed.
+
+**/
+EFI_STATUS
+Mtftp4RrqHandleOack (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ IN BOOLEAN Multicast,
+ OUT BOOLEAN *Completed
+ )
+{
+ MTFTP4_OPTION Reply;
+ EFI_STATUS Status;
+ INTN Expected;
+
+ *Completed = FALSE;
+
+ //
+ // If already started the master download, don't change the
+ // setting. Master download always succeeds.
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+ ASSERT (Expected != -1);
+
+ if (Instance->Master && (Expected != 1)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Parse and validate the options from server
+ //
+ NetZeroMem (&Reply, sizeof (MTFTP4_OPTION));
+
+ Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);
+
+ if (EFI_ERROR (Status) ||
+ !Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {
+ //
+ // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ "Mal-formated OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if (Reply.Exist & MTFTP4_MCAST_EXIST) {
+
+ //
+ // Save the multicast info. Always update the Master, only update the
+ // multicast IP address, block size, timeoute at the first time. If IP
+ // address is updated, create a UDP child to receive the multicast.
+ //
+ Instance->Master = Reply.Master;
+
+ if (Instance->McastIp == 0) {
+ if ((Reply.McastIp == 0) || (Reply.McastPort == 0)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ "Illegal multicast setting"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+
+ //
+ // Create a UDP child then start receive the multicast from it.
+ //
+ Instance->McastIp = Reply.McastIp;
+ Instance->McastPort = Reply.McastPort;
+ Instance->McastUdpPort = UdpIoCreatePort (
+ Instance->Service->Controller,
+ Instance->Service->Image,
+ Mtftp4RrqConfigMcastPort,
+ Instance
+ );
+
+ if (Instance->McastUdpPort == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
+
+ if (EFI_ERROR (Status)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION,
+ "Failed to create socket to receive multicast packet"
+ );
+
+ return Status;
+ }
+
+ //
+ // Update the parameters used.
+ //
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+ }
+
+ } else {
+ Instance->Master = TRUE;
+
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+ }
+
+ //
+ // Send an ACK to (Expected - 1) which is 0 for unicast download,
+ // or tell the server we want to receive the Expected block.
+ //
+ return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));
+}
+
+
+/**
+ The packet process callback for MTFTP download.
+
+ @param UdpPacket The packet received
+ @param Points The local/remote access point of the packet
+ @param IoStatus The status of the receiving
+ @param Context Opaque parameter, which is the MTFTP session
+
+ @return None
+
+**/
+VOID
+Mtftp4RrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_POINTS *Points,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PACKET *Packet;
+ BOOLEAN Completed;
+ BOOLEAN Multicast;
+ EFI_STATUS Status;
+ UINT16 Opcode;
+ UINT32 Len;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
+
+ Status = EFI_SUCCESS;
+ Packet = NULL;
+ Completed = FALSE;
+ Multicast = FALSE;
+
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ //
+ // Find the port this packet is from to restart receive correctly.
+ //
+ Multicast = (BOOLEAN) (Points->LocalAddr == Instance->McastIp);
+
+ if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client. The server
+ // is required to use the same port as RemotePort to multicast the
+ // data.
+ //
+ if (Points->RemotePort != Instance->ConnectedPort) {
+ if (Instance->ConnectedPort != 0) {
+ goto ON_EXIT;
+ } else {
+ Instance->ConnectedPort = Points->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+
+ if (UdpPacket->BlockOpNum > 1) {
+ Packet = NetAllocatePool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Call the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if ((Instance->Token->CheckPacket != NULL) &&
+ ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp4,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ switch (Opcode) {
+ case EFI_MTFTP4_OPCODE_DATA:
+ if ((Len > (UINT32) (MTFTP4_DATA_HEAD_LEN + Instance->BlkSize)) ||
+ (Len < (UINT32) MTFTP4_DATA_HEAD_LEN)) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4RrqHandleData (Instance, Packet, Len, Multicast, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ if (Multicast || (Len <= MTFTP4_OPCODE_LEN)) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4RrqHandleOack (Instance, Packet, Len, Multicast, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_ERROR:
+ Status = EFI_TFTP_ERROR;
+ break;
+ }
+
+ON_EXIT:
+
+ //
+ // Free the resources, then if !EFI_ERROR (Status), restart the
+ // receive, otherwise end the session.
+ //
+ if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
+ NetFreePool (Packet);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ if (!EFI_ERROR (Status) && !Completed) {
+ if (Multicast) {
+ Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
+ } else {
+ Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
+ }
+ }
+
+ if (EFI_ERROR (Status) || Completed) {
+ Mtftp4CleanOperation (Instance, Status);
+ }
+}
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c
new file mode 100644
index 0000000000..af61c0bf69
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c
@@ -0,0 +1,591 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Support.c
+
+Abstract:
+
+ Support routines for Mtftp
+
+
+**/
+
+#include "Mtftp4Impl.h"
+
+
+/**
+ Allocate a MTFTP4 block range, then init it to the
+ range of [Start, End]
+
+ @param Start The start block number
+ @param End The last block number in the range
+
+ @return NULL if failed to allocate memory, otherwise the created block range.
+
+**/
+STATIC
+MTFTP4_BLOCK_RANGE *
+Mtftp4AllocateRange (
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ Range = NetAllocatePool (sizeof (MTFTP4_BLOCK_RANGE));
+
+ if (Range == NULL) {
+ return NULL;
+ }
+
+ NetListInit (&Range->Link);
+ Range->Start = Start;
+ Range->End = End;
+
+ return Range;
+}
+
+
+/**
+ Initialize the block range for either RRQ or WRQ. RRQ and WRQ have
+ different requirements for Start and End. For example, during start
+ up, WRQ initializes its whole valid block range to [0, 0xffff]. This
+ is bacause the server will send us a ACK0 to inform us to start the
+ upload. When the client received ACK0, it will remove 0 from the range,
+ get the next block number, which is 1, then upload the BLOCK1. For RRQ
+ without option negotiation, the server will directly send us the BLOCK1
+ in response to the client's RRQ. When received BLOCK1, the client will
+ remove it from the block range and send an ACK. It also works if there
+ is option negotiation.
+
+ @param Head The block range head to initialize
+ @param Start The Start block number.
+ @param End The last block number.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
+ @retval EFI_SUCCESS The initial block range is created.
+
+**/
+EFI_STATUS
+Mtftp4InitBlockRange (
+ IN NET_LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ Range = Mtftp4AllocateRange (Start, End);
+
+ if (Range == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetListInsertTail (Head, &Range->Link);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the first valid block number on the range list.
+
+ @param Head The block range head
+
+ @return -1: if the block range is empty. Otherwise the first valid block number.
+
+**/
+INTN
+Mtftp4GetNextBlockNum (
+ IN NET_LIST_ENTRY *Head
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ if (NetListIsEmpty (Head)) {
+ return -1;
+ }
+
+ Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);
+ return Range->Start;
+}
+
+
+/**
+ Set the last block number of the block range list. It will
+ remove all the blocks after the Last. MTFTP initialize the
+ block range to the maximum possible range, such as [0, 0xffff]
+ for WRQ. When it gets the last block number, it will call
+ this function to set the last block number.
+
+ @param Head The block range list
+ @param Last The last block number
+
+ @return None
+
+**/
+VOID
+Mtftp4SetLastBlockNum (
+ IN NET_LIST_ENTRY *Head,
+ IN UINT16 Last
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ //
+ // Iterate from the tail to head to remove the block number
+ // after the last.
+ //
+ while (!NetListIsEmpty (Head)) {
+ Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);
+
+ if (Range->Start > Last) {
+ NetListRemoveEntry (&Range->Link);
+ NetFreePool (Range);
+ continue;
+ }
+
+ if (Range->End > Last) {
+ Range->End = Last;
+ }
+
+ return ;
+ }
+}
+
+
+/**
+ Remove the block number from the block range list.
+
+ @param Head The block range list to remove from
+ @param Num The block number to remove
+
+ @retval EFI_NOT_FOUND The block number isn't in the block range list
+ @retval EFI_SUCCESS The block number has been removed from the list
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
+
+**/
+EFI_STATUS
+Mtftp4RemoveBlockNum (
+ IN NET_LIST_ENTRY *Head,
+ IN UINT16 Num
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+ MTFTP4_BLOCK_RANGE *NewRange;
+ NET_LIST_ENTRY *Entry;
+
+ NET_LIST_FOR_EACH (Entry, Head) {
+
+ //
+ // Each block represents a hole [Start, End] in the file,
+ // skip to the first range with End >= Num
+ //
+ Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
+
+ if (Range->End < Num) {
+ continue;
+ }
+
+ //
+ // There are three different cases for Start
+ // 1. (Start > Num) && (End >= Num):
+ // because all the holes before this one has the condition of
+ // End < Num, so this block number has been removed.
+ //
+ // 2. (Start == Num) && (End >= Num):
+ // Need to increase the Start by one, and if End == Num, this
+ // hole has been removed completely, remove it.
+ //
+ // 3. (Start < Num) && (End >= Num):
+ // if End == Num, only need to decrease the End by one because
+ // we have (Start < Num) && (Num == End), so (Start <= End - 1).
+ // if (End > Num), the hold is splited into two holes, with
+ // [Start, Num - 1] and [Num + 1, End].
+ //
+ if (Range->Start > Num) {
+ return EFI_NOT_FOUND;
+
+ } else if (Range->Start == Num) {
+ Range->Start++;
+
+ if (Range->Start > Range->End) {
+ NetListRemoveEntry (&Range->Link);
+ NetFreePool (Range);
+ }
+
+ return EFI_SUCCESS;
+
+ } else {
+ if (Range->End == Num) {
+ Range->End--;
+ } else {
+ NewRange = Mtftp4AllocateRange (Num + 1, (UINT16) Range->End);
+
+ if (NewRange == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Range->End = Num - 1;
+ NetListInsertAfter (&Range->Link, &NewRange->Link);
+ }
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Build then transmit the request packet for the MTFTP session.
+
+ @param Instance The Mtftp session
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
+ @retval EFI_SUCCESS The request is built and sent
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendRequest (
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ EFI_MTFTP4_PACKET *Packet;
+ EFI_MTFTP4_OPTION *Options;
+ EFI_MTFTP4_TOKEN *Token;
+ NET_BUF *Nbuf;
+ UINT8 *Mode;
+ UINT8 *Cur;
+ UINT32 Len;
+ UINTN Index;
+
+ Token = Instance->Token;
+ Options = Token->OptionList;
+ Mode = Instance->Token->ModeStr;
+
+ if (Mode == NULL) {
+ Mode = "octet";
+ }
+
+ //
+ // Compute the packet length
+ //
+ Len = (UINT32) (AsciiStrLen (Token->Filename) + AsciiStrLen (Mode) + 4);
+
+ for (Index = 0; Index < Token->OptionCount; Index++) {
+ Len += (UINT32) (AsciiStrLen (Options[Index].OptionStr) +
+ AsciiStrLen (Options[Index].ValueStr) + 2);
+ }
+
+ //
+ // Allocate a packet then copy the data over
+ //
+ if ((Nbuf = NetbufAlloc (Len)) == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);
+ Packet->OpCode = HTONS (Instance->Operation);
+ Cur = Packet->Rrq.Filename;
+ Cur = AsciiStrCpy (Cur, Token->Filename);
+ Cur = AsciiStrCpy (Cur, Mode);
+
+ for (Index = 0; Index < Token->OptionCount; ++Index) {
+ Cur = AsciiStrCpy (Cur, Options[Index].OptionStr);
+ Cur = AsciiStrCpy (Cur, Options[Index].ValueStr);
+ }
+
+ return Mtftp4SendPacket (Instance, Nbuf);
+}
+
+
+/**
+ Build then send an error message
+
+ @param Instance The MTFTP session
+ @param ErrInfo The error code and message
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
+ @retval EFI_SUCCESS The error packet is transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendError (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8* ErrInfo
+ )
+{
+ NET_BUF *Packet;
+ EFI_MTFTP4_PACKET *TftpError;
+ UINT32 Len;
+
+ Len = (UINT32) (AsciiStrLen (ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));
+ Packet = NetbufAlloc (Len);
+
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);
+ TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);
+ TftpError->Error.ErrorCode = HTONS (ErrCode);
+
+ AsciiStrCpy (TftpError->Error.ErrorMessage, ErrInfo);
+
+ return Mtftp4SendPacket (Instance, Packet);
+}
+
+
+/**
+ The callback function called when the packet is transmitted.
+ It simply frees the packet.
+
+ @param Packet The transmitted (or failed to) packet
+ @param Points The local and remote UDP access point
+ @param IoStatus The result of the transmission
+ @param Context Opaque parameter to the callback
+
+ @return None
+
+**/
+STATIC
+VOID
+Mtftp4OnPacketSent (
+ NET_BUF *Packet,
+ UDP_POINTS *Points,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ NetbufFree (Packet);
+}
+
+
+/**
+ Set the timeout for the instance. User a longer time for
+ passive instances.
+
+ @param Instance The Mtftp session to set time out
+
+ @return None
+
+**/
+VOID
+Mtftp4SetTimeout (
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ if (Instance->Master) {
+ Instance->PacketToLive = Instance->Timeout;
+ } else {
+ Instance->PacketToLive = Instance->Timeout * 2;
+ }
+}
+
+
+/**
+ Send the packet for the instance. It will first save a reference to
+ the packet for later retransmission. then determine the destination
+ port, listen port for requests, and connected port for others. At last,
+ send the packet out.
+
+ @param Instance The Mtftp instance
+ @param Packet The packet to send
+
+ @retval EFI_SUCCESS The packet is sent out
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendPacket (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN NET_BUF *Packet
+ )
+{
+ UDP_POINTS UdpPoint;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+
+ //
+ // Save the packet for retransmission
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ }
+
+ Instance->LastPacket = Packet;
+
+ Instance->CurRetry = 0;
+ Mtftp4SetTimeout (Instance);
+
+ UdpPoint.LocalAddr = 0;
+ UdpPoint.LocalPort = 0;
+ UdpPoint.RemoteAddr = Instance->ServerIp;
+
+ //
+ // Send the requests to the listening port, other packets
+ // to the connected port
+ //
+ OpCode = NTOHS (*((UINT16 *) NetbufGetByte (Packet, 0, NULL)));
+
+ if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
+ (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
+ UdpPoint.RemotePort = Instance->ListeningPort;
+ } else {
+ UdpPoint.RemotePort = Instance->ConnectedPort;
+ }
+
+ NET_GET_REF (Packet);
+
+ Status = UdpIoSendDatagram (
+ Instance->UnicastPort,
+ Packet,
+ &UdpPoint,
+ Instance->Gateway,
+ Mtftp4OnPacketSent,
+ Instance
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Packet);
+ }
+
+ return Status;
+}
+
+
+/**
+ Retransmit the last packet for the instance
+
+ @param Instance The Mtftp instance
+
+ @retval EFI_SUCCESS The last packet is retransmitted.
+ @retval Others Failed to retransmit.
+
+**/
+EFI_STATUS
+Mtftp4Retransmit (
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ UDP_POINTS UdpPoint;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+
+ ASSERT (Instance->LastPacket != NULL);
+
+ UdpPoint.LocalAddr = 0;
+ UdpPoint.LocalPort = 0;
+ UdpPoint.RemoteAddr = Instance->ServerIp;
+
+ //
+ // Set the requests to the listening port, other packets to the connected port
+ //
+ OpCode = NTOHS (*(UINT16 *) NetbufGetByte (Instance->LastPacket, 0, NULL));
+
+ if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
+ (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
+ UdpPoint.RemotePort = Instance->ListeningPort;
+ } else {
+ UdpPoint.RemotePort = Instance->ConnectedPort;
+ }
+
+ NET_GET_REF (Instance->LastPacket);
+
+ Status = UdpIoSendDatagram (
+ Instance->UnicastPort,
+ Instance->LastPacket,
+ &UdpPoint,
+ Instance->Gateway,
+ Mtftp4OnPacketSent,
+ Instance
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Instance->LastPacket);
+ }
+
+ return Status;
+}
+
+
+/**
+ The timer ticking function for the Mtftp service instance.
+
+ @param Event The ticking event
+ @param Context The Mtftp service instance
+
+ @return None
+
+**/
+VOID
+EFIAPI
+Mtftp4OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_TOKEN *Token;
+
+ MtftpSb = (MTFTP4_SERVICE *) Context;
+
+ //
+ // Iterate through all the children of the Mtftp service instance. Time
+ // out the packet. If maximum retries reached, clean the session up.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {
+ Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);
+
+ if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {
+ continue;
+ }
+
+ //
+ // Call the user's time out handler
+ //
+ Token = Instance->Token;
+
+ if ((Token->TimeoutCallback != NULL) &&
+ EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {
+
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ "User aborted the transfer in time out"
+ );
+
+ Mtftp4CleanOperation (Instance, EFI_ABORTED);
+ continue;
+ }
+
+ //
+ // Retransmit the packet if haven't reach the maxmium retry count,
+ // otherwise exit the transfer.
+ //
+ if (++Instance->CurRetry < Instance->MaxRetry) {
+ Mtftp4Retransmit (Instance);
+ Mtftp4SetTimeout (Instance);
+ } else {
+ Mtftp4CleanOperation (Instance, EFI_TIMEOUT);
+ continue;
+ }
+ }
+}
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h
new file mode 100644
index 0000000000..be186866c5
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h
@@ -0,0 +1,96 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Support.h
+
+Abstract:
+
+ Support routines for MTFTP
+
+
+**/
+
+#ifndef __EFI_MTFTP4_SUPPORT_H__
+#define __EFI_MTFTP4_SUPPORT_H__
+
+//
+// The structure representing a range of block numbers, [Start, End].
+// It is used to remember the holes in the MTFTP block space. If all
+// the holes are filled in, then the download or upload has completed.
+//
+typedef struct {
+ NET_LIST_ENTRY Link;
+ INTN Start;
+ INTN End;
+} MTFTP4_BLOCK_RANGE;
+
+
+EFI_STATUS
+Mtftp4InitBlockRange (
+ IN NET_LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ );
+
+INTN
+Mtftp4GetNextBlockNum (
+ IN NET_LIST_ENTRY *Head
+ );
+
+VOID
+Mtftp4SetLastBlockNum (
+ IN NET_LIST_ENTRY *Head,
+ IN UINT16 Last
+ );
+
+EFI_STATUS
+Mtftp4RemoveBlockNum (
+ IN NET_LIST_ENTRY *Head,
+ IN UINT16 Num
+ );
+
+VOID
+Mtftp4SetTimeout (
+ IN MTFTP4_PROTOCOL *Instance
+ );
+
+EFI_STATUS
+Mtftp4SendPacket (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN NET_BUF *Packet
+ );
+
+EFI_STATUS
+Mtftp4SendRequest (
+ IN MTFTP4_PROTOCOL *Instance
+ );
+
+EFI_STATUS
+Mtftp4SendError (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8* ErrInfo
+ );
+
+EFI_STATUS
+Mtftp4Retransmit (
+ IN MTFTP4_PROTOCOL *Instance
+ );
+
+VOID
+EFIAPI
+Mtftp4OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+#endif
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c
new file mode 100644
index 0000000000..5ac5dc8ec0
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c
@@ -0,0 +1,522 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Mtftp4Wrq.c
+
+Abstract:
+
+ Routines to process Wrq (upload)
+
+
+**/
+
+#include "Mtftp4Impl.h"
+
+VOID
+Mtftp4WrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_POINTS *Points,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+
+/**
+ Start the MTFTP session for pload. It will first init some states,
+ then send the WRQ request packet, and start receiving the packet.
+
+ @param Instance The MTFTP session
+ @param Operation Redundant parameter, which is always
+ EFI_MTFTP4_OPCODE_WRQ here.
+
+ @retval EFI_SUCCESS The upload process has been started.
+ @retval Others Failed to start the upload.
+
+**/
+EFI_STATUS
+Mtftp4WrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [0, 0xffff]. For example:
+ // the client sends an WRQ request to the server, the server
+ // ACK with an ACK0 to let client start transfer the first
+ // packet.
+ //
+ Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp4SendRequest (Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
+}
+
+
+/**
+ Build then send a MTFTP data packet for the MTFTP upload session.
+
+ @param Instance The MTFTP upload session
+ @param BlockNum The block number to send
+
+ @retval EFI_OUT_OF_RESOURCES Failed to build the packet
+ @retval EFI_ABORTED The consumer of this child directs to abort the
+ transmission by return an error through
+ PacketNeeded
+ @retval EFI_SUCCESS The data is sent.
+
+**/
+EFI_STATUS
+Mtftp4WrqSendBlock (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 BlockNum
+ )
+{
+ EFI_MTFTP4_PACKET *Packet;
+ EFI_MTFTP4_TOKEN *Token;
+ NET_BUF *UdpPacket;
+ EFI_STATUS Status;
+ UINT16 DataLen;
+ UINT8 *DataBuf;
+ UINT64 Start;
+
+ //
+ // Allocate a buffer to hold the user data
+ //
+ UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);
+
+ if (UdpPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet = (EFI_MTFTP4_PACKET *)NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);
+
+ Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);
+ Packet->Data.Block = HTONS (BlockNum);
+
+ //
+ // Read the block from either the buffer or PacketNeeded callback
+ //
+ Token = Instance->Token;
+ DataLen = Instance->BlkSize;
+
+ if (Token->Buffer != NULL) {
+ Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);
+
+ if (Token->BufferSize < Start + Instance->BlkSize) {
+ DataLen = (UINT16) (Token->BufferSize - Start);
+ Instance->LastBlock = BlockNum;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ NetCopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);
+ }
+
+ } else {
+ //
+ // Get data from PacketNeeded
+ //
+ DataBuf = NULL;
+ Status = Token->PacketNeeded (&Instance->Mtftp4, Token, &DataLen, &DataBuf);
+
+ if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {
+ if (DataBuf != NULL) {
+ gBS->FreePool (DataBuf);
+ }
+
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ "User aborted the transfer"
+ );
+
+ return EFI_ABORTED;
+ }
+
+ if (DataLen < Instance->BlkSize) {
+ Instance->LastBlock = BlockNum;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ NetCopyMem (Packet->Data.Data, DataBuf, DataLen);
+ gBS->FreePool (DataBuf);
+ }
+ }
+
+ return Mtftp4SendPacket (Instance, UdpPacket);
+}
+
+
+/**
+ Function to handle received ACK packet. If the ACK number matches the
+ expected block number, and there are more data pending, send the next
+ block. Otherwise tell the caller that we are done.
+
+ @param Instance The MTFTP upload session
+ @param Packet The MTFTP packet received
+ @param Len The packet length
+ @param Completed Return whether the upload has finished.
+
+ @retval EFI_SUCCESS The ACK is successfully processed.
+ @retval EFI_TFTP_ERROR The block number loops back.
+ @retval Others Failed to transmit the next data packet.
+
+**/
+EFI_STATUS
+Mtftp4WrqHandleAck (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ OUT BOOLEAN *Completed
+ )
+{
+ UINT16 AckNum;
+ INTN Expected;
+
+ *Completed = FALSE;
+ AckNum = NTOHS (Packet->Ack.Block[0]);
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput
+ // restart receive.
+ //
+ if (Expected != AckNum) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Remove the acked block number, if this is the last block number,
+ // tell the Mtftp4WrqInput to finish the transfer. This is the last
+ // block number if the block range are empty..
+ //
+ Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum);
+
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Expected < 0) {
+ //
+ // The block range is empty. It may either because the the last
+ // block has been ACKed, or the sequence number just looped back,
+ // that is, there is more than 0xffff blocks.
+ //
+ if (Instance->LastBlock == AckNum) {
+ ASSERT (Instance->LastBlock >= 1);
+ *Completed = TRUE;
+ return EFI_SUCCESS;
+
+ } else {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ "Block number rolls back, not supported, try blksize option"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+ }
+
+ return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);
+}
+
+
+/**
+ Check whether the received OACK is valid. The OACK is valid
+ only if:
+ 1. It only include options requested by us
+ 2. It can only include a smaller block size
+ 3. It can't change the proposed time out value.
+ 4. Other requirements of the individal MTFTP options as required.s
+
+ @param Reply The options included in the OACK
+ @param Request The options we requested
+
+ @return TRUE if the options included in OACK is valid, otherwise FALSE.
+
+**/
+BOOLEAN
+Mtftp4WrqOackValid (
+ IN MTFTP4_OPTION *Reply,
+ IN MTFTP4_OPTION *Request
+ )
+{
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((Reply->Exist &~Request->Exist) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size to be used and
+ // return the timeout matches that requested.
+ //
+ if (((Reply->Exist & MTFTP4_BLKSIZE_EXIST) && (Reply->BlkSize > Request->BlkSize)) ||
+ ((Reply->Exist & MTFTP4_TIMEOUT_EXIST) && (Reply->Timeout != Request->Timeout))) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Function to handle the MTFTP OACK packet. It parses the packet's
+ options, and update the internal states of the session
+
+ @param Instance The MTFTP session
+ @param Packet The received OACK packet
+ @param Len The length of the packet
+ @param Completed Whether the transmisson has completed. NOT used by
+ this function.
+
+ @retval EFI_SUCCESS The OACK process is OK
+ @retval EFI_TFTP_ERROR Some error occured, and the session reset.
+
+**/
+EFI_STATUS
+Mtftp4WrqHandleOack (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ OUT BOOLEAN *Completed
+ )
+{
+ MTFTP4_OPTION Reply;
+ EFI_MTFTP4_PACKET Bogus;
+ EFI_STATUS Status;
+ INTN Expected;
+
+ *Completed = FALSE;
+
+ //
+ // Ignore the OACK if already started the upload
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Expected != 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Parse and validate the options from server
+ //
+ NetZeroMem (&Reply, sizeof (MTFTP4_OPTION));
+ Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);
+
+ if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {
+ //
+ // Don't send a MTFTP error packet when out of resource, it can
+ // only make it worse.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ "Mal-formated OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+
+ //
+ // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,
+ // which will start the transmission of the first data block.
+ //
+ Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
+ Bogus.Ack.Block[0] = 0;
+
+ return Mtftp4WrqHandleAck (Instance, &Bogus, sizeof (EFI_MTFTP4_ACK_HEADER), Completed);
+}
+
+
+/**
+ The input process routine for MTFTP upload.
+
+ @param UdpPacket The received MTFTP packet.
+ @param Points The local/remote access point
+ @param IoStatus The result of the packet receiving
+ @param Context Opaque parameter for the callback, which is the
+ MTFTP session.
+
+ @return None
+
+**/
+VOID
+Mtftp4WrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_POINTS *Points,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PACKET *Packet;
+ BOOLEAN Completed;
+ EFI_STATUS Status;
+ UINT32 Len;
+ UINT16 Opcode;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
+
+ Completed = FALSE;
+ Packet = NULL;
+ Status = EFI_SUCCESS;
+
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client.
+ //
+ if (Points->RemotePort != Instance->ConnectedPort) {
+ if (Instance->ConnectedPort != 0) {
+ goto ON_EXIT;
+ } else {
+ Instance->ConnectedPort = Points->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+
+ if (UdpPacket->BlockOpNum > 1) {
+ Packet = NetAllocatePool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Call the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if ((Instance->Token->CheckPacket != NULL) &&
+ ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp4,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ switch (Opcode) {
+ case EFI_MTFTP4_OPCODE_ACK:
+ if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ if (Len <= MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_ERROR:
+ Status = EFI_TFTP_ERROR;
+ break;
+ }
+
+ON_EXIT:
+ //
+ // Free the resources, then if !EFI_ERROR (Status) and not completed,
+ // restart the receive, otherwise end the session.
+ //
+ if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
+ NetFreePool (Packet);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ if (!EFI_ERROR (Status) && !Completed) {
+ Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
+ }
+
+ //
+ // Status may have been updated by UdpIoRecvDatagram
+ //
+ if (EFI_ERROR (Status) || Completed) {
+ Mtftp4CleanOperation (Instance, Status);
+ }
+}
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c
new file mode 100644
index 0000000000..5f15c81e48
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c
@@ -0,0 +1,2401 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ bc.c
+
+Abstract:
+
+
+**/
+
+#include "Bc.h"
+
+//
+//
+//
+EFI_STATUS
+EFIAPI
+PxeBcDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+PxeBcDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+PxeBcDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+extern
+VOID
+InitArpHeader (
+ VOID
+ );
+extern
+VOID
+OptionsStrucInit (
+ VOID
+ );
+
+//
+// helper routines
+//
+
+/**
+ Convert number to ASCII value
+
+ @param Number Numeric value to convert to decimal ASCII value.
+ @param Buffer Buffer to place ASCII version of the Number
+ @param Length Length of Buffer.
+
+ @retval none none
+
+**/
+VOID
+CvtNum (
+ IN UINTN Number,
+ IN UINT8 *Buffer,
+ IN INTN Length
+ )
+{
+ UINTN Remainder;
+
+ while (Length--) {
+ Remainder = Number % 10;
+ Number /= 10;
+ Buffer[Length] = (UINT8) ('0' + Remainder);
+ }
+}
+
+
+/**
+ Convert number to decimal ASCII value at Buffer location
+
+ @param Number Numeric value to convert to decimal ASCII value.
+ @param Buffer Buffer to place ASCII version of the Number
+
+ @retval none none
+
+**/
+VOID
+UtoA10 (
+ IN UINTN Number,
+ IN UINT8 *Buffer
+ )
+{
+ INTN Index;
+ UINT8 BuffArray[31];
+
+ BuffArray[30] = 0;
+ CvtNum (Number, BuffArray, 30);
+
+ for (Index = 0; Index < 30; ++Index) {
+ if (BuffArray[Index] != '0') {
+ break;
+ }
+ }
+
+ CopyMem (Buffer, BuffArray + Index, 31 - Index);
+}
+
+
+/**
+ Convert ASCII numeric string to a UINTN value
+
+ @param Number Numeric value to convert to decimal ASCII value.
+ @param Buffer Buffer to place ASCII version of the Number
+
+ @retval Value UINTN value of the ASCII string.
+
+**/
+UINTN
+AtoU (
+ IN UINT8 *Buffer
+ )
+{
+ UINTN Value;
+ INT8 Character;
+
+ Value = 0;
+ Character = *Buffer++;
+ do {
+ Value = Value * 10 + Character - '0';
+ Character = *Buffer++;
+ } while (Character);
+
+ return Value;
+}
+
+
+/**
+ Convert ASCII numeric string to a UINTN value
+
+ @param Number Numeric value to convert to decimal ASCII value.
+ @param Buffer Buffer to place ASCII version of the Number
+
+ @retval Value UINTN value of the ASCII string.
+
+**/
+UINT64
+AtoU64 (
+ IN UINT8 *Buffer
+ )
+{
+ UINT64 Value;
+ UINT8 Character;
+
+ Value = 0;
+ while ((Character = *Buffer++) != '\0') {
+ Value = MultU64x32 (Value, 10) + (Character - '0');
+ }
+
+ return Value;
+}
+//
+// random number generator
+//
+#define RANDOM_MULTIPLIER 2053
+#define RANDOM_ADD_IN_VALUE 19
+
+VOID
+SeedRandom (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN UINT16 InitialSeed
+ )
+/*++
+
+ Routine Description:
+ Initialize the Seed for the random number generator
+
+ Arguments:
+
+ Returns:
+ none -
+
+--*/
+{
+ if (Private != NULL) {
+ Private->RandomSeed = InitialSeed;
+ }
+}
+
+
+/**
+ Generate and return a pseudo-random number
+
+
+ @retval Number UINT16 random number
+
+**/
+UINT16
+Random (
+ IN PXE_BASECODE_DEVICE *Private
+ )
+{
+ UINTN Number;
+
+ if (Private != NULL) {
+ Number = -(INTN) Private->RandomSeed * RANDOM_MULTIPLIER + RANDOM_ADD_IN_VALUE;
+
+ return Private->RandomSeed = (UINT16) Number;
+ } else {
+ return 0;
+ }
+}
+//
+// calculate the internet checksum (RFC 1071)
+// return 16 bit ones complement of ones complement sum of 16 bit words
+//
+
+/**
+ Calculate the internet checksum (see RFC 1071)
+
+ @param Packet Buffer which contains the data to be checksummed
+ @param Length Length to be checksummed
+
+ @retval Checksum Returns the 16 bit ones complement of ones
+ complement sum of 16 bit words
+
+**/
+UINT16
+IpChecksum (
+ IN UINT16 *Packet,
+ IN UINTN Length
+ )
+{
+ UINT32 Sum;
+ UINT8 Odd;
+
+ Sum = 0;
+ Odd = (UINT8) (Length & 1);
+ Length >>= 1;
+ while (Length--) {
+ Sum += *Packet++;
+ }
+
+ if (Odd) {
+ Sum += *(UINT8 *) Packet;
+ }
+
+ Sum = (Sum & 0xffff) + (Sum >> 16);
+ //
+ // in case above carried
+ //
+ Sum += Sum >> 16;
+
+ return (UINT16) (~ (UINT16) Sum);
+}
+
+
+/**
+ Calculate the internet checksum (see RFC 1071)
+ on a non contiguous header and data
+
+ @param Header Buffer which contains the data to be checksummed
+ @param HeaderLen Length to be checksummed
+ @param Message Buffer which contains the data to be checksummed
+ @param MessageLen Length to be checksummed
+
+ @retval Checksum Returns the 16 bit ones complement of ones
+ complement sum of 16 bit words
+
+**/
+UINT16
+IpChecksum2 (
+ IN UINT16 *Header,
+ IN UINTN HeaderLen,
+ IN UINT16 *Message,
+ IN UINTN MessageLen
+ )
+{
+ UINT32 Sum;
+
+ Sum = (UINT16)~IpChecksum (Header, HeaderLen) + (UINT16)~IpChecksum (Message, MessageLen);
+
+ //
+ // in case above carried
+ //
+ Sum += Sum >> 16;
+
+ return (UINT16) (~ (UINT16) Sum);
+}
+
+
+/**
+ Adjust the internet checksum (see RFC 1071) on a single word update.
+
+ @param OldChkSum Checksum previously calculated
+ @param OldWord Value
+ @param NewWord New Value
+
+ @retval Checksum Returns the 16 bit ones complement of ones
+ complement sum of 16 bit words
+
+**/
+UINT16
+UpdateChecksum (
+ IN UINT16 OldChksum,
+ IN UINT16 OldWord,
+ IN UINT16 NewWord
+ )
+{
+ UINT32 sum;
+
+ sum = ~OldChksum + NewWord - OldWord;
+ //
+ // in case above carried
+ //
+ sum += sum >> 16;
+ return (UINT16) (~ (UINT16) sum);
+}
+
+
+/**
+ See if a callback is in play
+
+ @param Private Pointer to Pxe BaseCode Protocol
+
+ @retval 0 Callbacks are active on the handle
+ @retval 1 Callbacks are not active on the handle
+
+**/
+STATIC
+BOOLEAN
+SetMakeCallback (
+ IN PXE_BASECODE_DEVICE *Private
+ )
+{
+ Private->EfiBc.Mode->MakeCallbacks = (BOOLEAN) (gBS->HandleProtocol (
+ Private->Handle,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ (VOID *) &Private->CallbackProtocolPtr
+ ) == EFI_SUCCESS);
+
+ DEBUG (
+ (DEBUG_INFO,
+ "\nMode->MakeCallbacks == %d ",
+ Private->EfiBc.Mode->MakeCallbacks)
+ );
+
+ DEBUG (
+ (DEBUG_INFO,
+ "\nPrivate->CallbackProtocolPtr == %xh ",
+ Private->CallbackProtocolPtr)
+ );
+
+ if (Private->CallbackProtocolPtr != NULL) {
+ DEBUG (
+ (DEBUG_INFO,
+ "\nCallbackProtocolPtr->Revision = %xh ",
+ Private->CallbackProtocolPtr->Revision)
+ );
+
+ DEBUG (
+ (DEBUG_INFO,
+ "\nCallbackProtocolPtr->Callback = %xh ",
+ Private->CallbackProtocolPtr->Callback)
+ );
+ }
+
+ return Private->EfiBc.Mode->MakeCallbacks;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+ Routine which does an SNP->Receive over a timeout period and doing callbacks
+
+ @param Private Pointer to Pxe BaseCode Protocol
+ @param Function What PXE function to callback
+ @param TimeoutEvent Timer event that will trigger when we have waited
+ too long for an incoming packet
+ @param HeaderSizePtr Pointer to the size of the Header size
+ @param BufferSizePtr Pointer to the size of the Buffer size
+ @param ProtocolPtr The protocol to sniff for (namely, UDP/TCP/etc)
+
+ @retval 0 Something was returned
+ @retval !0 Like there was nothing to receive
+ (EFI_TIMEOUT/NOT_READY)
+
+**/
+EFI_STATUS
+WaitForReceive (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN EFI_PXE_BASE_CODE_FUNCTION Function,
+ IN EFI_EVENT TimeoutEvent,
+ IN OUT UINTN *HeaderSizePtr,
+ IN OUT UINTN *BufferSizePtr,
+ IN OUT UINT16 *ProtocolPtr
+ )
+{
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;
+ EFI_PXE_CALLBACK CallbackPtr;
+ EFI_STATUS StatCode;
+ EFI_EVENT CallbackEvent;
+
+ //
+ // Initialize pointer to SNP interface
+ //
+ SnpPtr = Private->SimpleNetwork;
+
+ //
+ // Initialize pointer to PxeBc callback routine - if any
+ //
+ CallbackPtr = (Private->EfiBc.Mode->MakeCallbacks) ? Private->CallbackProtocolPtr->Callback : NULL;
+
+ //
+ // Create callback event and set timer
+ //
+ StatCode = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &CallbackEvent
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // every 100 milliseconds
+ //
+ StatCode = gBS->SetTimer (
+ CallbackEvent,
+ TimerPeriodic,
+ 1000000
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ gBS->CloseEvent (CallbackEvent);
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Loop until a packet is received or a receive error is detected or
+ // a callback abort is detected or a timeout event occurs.
+ //
+ for (;;)
+ {
+ //
+ // Poll for received packet.
+ //
+ *BufferSizePtr = BUFFER_ALLOCATE_SIZE;
+
+ StatCode = SnpPtr->Receive (
+ SnpPtr,
+ HeaderSizePtr,
+ BufferSizePtr,
+ Private->ReceiveBufferPtr,
+ 0,
+ 0,
+ ProtocolPtr
+ );
+
+ if (!EFI_ERROR (StatCode)) {
+ //
+ // Packet was received. Make received callback then return.
+ //
+ if (CallbackPtr != NULL) {
+ StatCode = CallbackPtr (
+ Private->CallbackProtocolPtr,
+ Function,
+ TRUE,
+ (UINT32) *BufferSizePtr,
+ (EFI_PXE_BASE_CODE_PACKET *) Private->ReceiveBufferPtr
+ );
+
+ if (StatCode != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+ StatCode = EFI_ABORTED;
+ } else {
+ StatCode = EFI_SUCCESS;
+ }
+ }
+
+ break;
+ }
+
+ if (StatCode != EFI_NOT_READY) {
+ break;
+ }
+ //
+ // Check for callback event.
+ //
+ if (!EFI_ERROR (gBS->CheckEvent (CallbackEvent))) {
+ //
+ // Make periodic callback if callback pointer is initialized.
+ //
+ if (CallbackPtr != NULL) {
+ StatCode = CallbackPtr (
+ Private->CallbackProtocolPtr,
+ Function,
+ FALSE,
+ 0,
+ NULL
+ );
+
+ //
+ // Abort if directed to by callback routine.
+ //
+ if (StatCode != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+ StatCode = EFI_ABORTED;
+ break;
+ }
+ }
+ }
+ //
+ // Check for timeout event.
+ //
+ if (TimeoutEvent == 0) {
+ StatCode = EFI_TIMEOUT;
+ break;
+ }
+
+ if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ StatCode = EFI_TIMEOUT;
+ break;
+ }
+ //
+ // Check IGMP timer events.
+ //
+ IgmpCheckTimers (Private);
+ }
+
+ gBS->CloseEvent (CallbackEvent);
+
+ return StatCode;
+}
+
+
+/**
+ Routine which does an SNP->Transmit of a buffer
+
+ @param Private Pointer to Pxe BaseCode Protocol
+ @param HeaderPtr Pointer to the buffer
+ @param PacketPtr Pointer to the packet to send
+ @param PacketLen The length of the entire packet to send
+ @param HardwareAddr Pointer to the MAC address of the destination
+ @param MediaProtocol What type of frame to create (RFC 1700) - IE.
+ Ethernet
+ @param Function What PXE function to callback
+
+ @retval 0 Something was sent
+ @retval !0 An error was encountered during sending of a packet
+
+**/
+EFI_STATUS
+SendPacket (
+ PXE_BASECODE_DEVICE *Private,
+ VOID *HeaderPtr,
+ VOID *PacketPtr,
+ INTN PacketLen,
+ VOID *HardwareAddr,
+ UINT16 MediaProtocol,
+ IN EFI_PXE_BASE_CODE_FUNCTION Function
+ )
+{
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;
+ EFI_SIMPLE_NETWORK_MODE *SnpModePtr;
+ EFI_PXE_CALLBACK CallbackPtr;
+ EFI_STATUS StatCode;
+ EFI_EVENT TimeoutEvent;
+ UINT32 IntStatus;
+ VOID *TxBuf;
+
+ //
+ //
+ //
+ CallbackPtr = Private->EfiBc.Mode->MakeCallbacks ? Private->CallbackProtocolPtr->Callback : 0;
+
+ SnpPtr = Private->SimpleNetwork;
+ SnpModePtr = SnpPtr->Mode;
+
+ //
+ // clear prior interrupt status
+ //
+ StatCode = SnpPtr->GetStatus (SnpPtr, &IntStatus, 0);
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nSendPacket() Exit #1 %xh (%r)",
+ StatCode,
+ StatCode)
+ );
+ return StatCode;
+ }
+
+ Private->DidTransmit = FALSE;
+
+ if (CallbackPtr != NULL) {
+ if (CallbackPtr (
+ Private->CallbackProtocolPtr,
+ Function,
+ FALSE,
+ (UINT32) PacketLen,
+ PacketPtr
+ ) != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nSendPacket() Exit #2 %xh (%r)",
+ EFI_ABORTED,
+ EFI_ABORTED)
+ );
+ return EFI_ABORTED;
+ }
+ }
+ //
+ // put packet in transmit queue
+ // headersize should be zero if not filled in
+ //
+ StatCode = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_ERROR,
+ "Could not create transmit timeout event. %r\n",
+ StatCode)
+ );
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // 5 milliseconds
+ //
+ StatCode = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ 50000
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_ERROR,
+ "Could not set transmit timeout event timer. %r\n",
+ StatCode)
+ );
+ gBS->CloseEvent (TimeoutEvent);
+ return EFI_DEVICE_ERROR;
+ }
+
+ for (;;) {
+ StatCode = SnpPtr->Transmit (
+ SnpPtr,
+ (UINTN) SnpPtr->Mode->MediaHeaderSize,
+ (UINTN) (PacketLen + SnpPtr->Mode->MediaHeaderSize),
+ HeaderPtr,
+ &SnpModePtr->CurrentAddress,
+ (EFI_MAC_ADDRESS *) HardwareAddr,
+ &MediaProtocol
+ );
+
+ if (StatCode != EFI_NOT_READY) {
+ break;
+ }
+
+ if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ StatCode = EFI_TIMEOUT;
+ break;
+ }
+ }
+
+ gBS->CloseEvent (TimeoutEvent);
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nSendPacket() Exit #3 %xh (%r)",
+ StatCode,
+ StatCode)
+ );
+ return StatCode;
+ }
+ //
+ // remove transmit buffer from snp's unused queue
+ // done this way in case someday things are buffered and we don't get it back
+ // immediately
+ //
+ StatCode = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_ERROR,
+ "Could not create transmit status timeout event. %r\n",
+ StatCode)
+ );
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // 5 milliseconds
+ //
+ StatCode = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ 50000
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_ERROR,
+ "Could not set transmit status timeout event timer. %r\n",
+ StatCode)
+ );
+ gBS->CloseEvent (TimeoutEvent);
+ return EFI_DEVICE_ERROR;
+ }
+
+ for (;;) {
+ StatCode = SnpPtr->GetStatus (SnpPtr, &IntStatus, &TxBuf);
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nSendPacket() Exit #4 %xh (%r)",
+ StatCode,
+ StatCode)
+ );
+ break;
+ }
+
+ if (IntStatus & EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT) {
+ Private->DidTransmit = TRUE;
+ }
+
+ if (TxBuf != NULL) {
+ break;
+ }
+
+ if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ StatCode = EFI_TIMEOUT;
+ break;
+ }
+ }
+
+ gBS->CloseEvent (TimeoutEvent);
+
+ return StatCode;
+}
+//
+//
+//
+
+/**
+
+
+**/
+EFI_BIS_PROTOCOL *
+PxebcBisStart (
+ IN PXE_BASECODE_DEVICE *Private,
+ OUT BIS_APPLICATION_HANDLE *BisAppHandle,
+ OUT OPTIONAL EFI_BIS_DATA **BisDataSigInfo
+ )
+{
+ EFI_STATUS EfiStatus;
+ EFI_HANDLE BisHandleBuffer;
+ UINTN BisHandleCount;
+ EFI_BIS_PROTOCOL *BisPtr;
+ EFI_BIS_VERSION BisInterfaceVersion;
+ BOOLEAN BisCheckFlag;
+
+ BisHandleCount = sizeof (EFI_HANDLE);
+ BisCheckFlag = FALSE;
+
+ //
+ // Locate BIS protocol handle (if present).
+ // If BIS protocol handle is not found, return NULL.
+ //
+ DEBUG ((DEBUG_INFO, "\ngBS->LocateHandle() "));
+
+ EfiStatus = gBS->LocateHandle (
+ ByProtocol,
+ &gEfiBisProtocolGuid,
+ NULL,
+ &BisHandleCount,
+ &BisHandleBuffer
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ //
+ // Any error means that there is no BIS.
+ // Note - It could mean that there are more than
+ // one BIS protocols installed, but that scenario
+ // is not yet supported.
+ //
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxebcBisStart()""\n gBS->LocateHandle() %r (%xh)\n",
+ EfiStatus,
+ EfiStatus)
+ );
+
+ return NULL;
+ }
+
+ if (BisHandleCount != sizeof BisHandleBuffer) {
+ //
+ // This really should never happen, but I am paranoid.
+ //
+ DEBUG (
+ (DEBUG_NET,
+ "\nPxebcBisStart() BisHandleCount != %d\n",
+ sizeof BisHandleBuffer)
+ );
+
+ return NULL;
+ }
+
+ DEBUG ((DEBUG_INFO, "BIS handle found."));
+
+ //
+ // Locate BIS protocol interface.
+ // If the BIS protocol interface cannot be found, return NULL.
+ //
+ DEBUG ((DEBUG_INFO, "\ngBS->HandleProtocol() "));
+
+ EfiStatus = gBS->HandleProtocol (
+ BisHandleBuffer,
+ &gEfiBisProtocolGuid,
+ (VOID **) &BisPtr
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxebcBisStart()""\n gBS->HandleProtocol() %r (%xh)\n",
+ EfiStatus,
+ EfiStatus)
+ );
+
+ return NULL;
+ }
+
+ if (BisPtr == NULL) {
+ //
+ // This really should never happen.
+ //
+ DEBUG (
+ (DEBUG_NET,
+ "\nPxebcBisStart()""\n gBS->HandleProtocoL() ""BIS protocol interface pointer is NULL!\n")
+ );
+
+ return NULL;
+ }
+
+ DEBUG ((DEBUG_INFO, "BIS protocol interface found."));
+
+ //
+ // Check that all of the BIS API function pointers are not NULL.
+ //
+ if (BisPtr->Initialize == NULL ||
+ BisPtr->Shutdown == NULL ||
+ BisPtr->Free == NULL ||
+ BisPtr->GetBootObjectAuthorizationCertificate == NULL ||
+ BisPtr->GetBootObjectAuthorizationCheckFlag == NULL ||
+ BisPtr->GetBootObjectAuthorizationUpdateToken == NULL ||
+ BisPtr->GetSignatureInfo == NULL ||
+ BisPtr->UpdateBootObjectAuthorization == NULL ||
+ BisPtr->VerifyBootObject == NULL ||
+ BisPtr->VerifyObjectWithCredential == NULL
+ ) {
+ DEBUG (
+ (
+ DEBUG_NET,
+ "\nPxebcBisStart()""\n BIS protocol interface is invalid."
+ "\n At least one BIS protocol function pointer is NULL.\n"
+ )
+ );
+
+ return NULL;
+ }
+ //
+ // Initialize BIS.
+ // If BIS does not initialize, return NULL.
+ //
+ DEBUG ((DEBUG_INFO, "\nBisPtr->Initialize() "));
+
+ BisInterfaceVersion.Major = BIS_VERSION_1;
+
+ EfiStatus = BisPtr->Initialize (
+ BisPtr,
+ BisAppHandle,
+ &BisInterfaceVersion,
+ NULL
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxebcBisStart()""\n BisPtr->Initialize() %r (%xh)\n",
+ EfiStatus,
+ EfiStatus)
+ );
+
+ return NULL;
+ }
+
+ DEBUG (
+ (DEBUG_INFO,
+ " BIS version: %d.%d",
+ BisInterfaceVersion.Major,
+ BisInterfaceVersion.Minor)
+ );
+
+ //
+ // If the requested BIS API version is not supported,
+ // shutdown BIS and return NULL.
+ //
+ if (BisInterfaceVersion.Major != BIS_VERSION_1) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxebcBisStart()""\n BIS version %d.%d not supported by PXE BaseCode.\n",
+ BisInterfaceVersion.Major,
+ BisInterfaceVersion.Minor)
+ );
+
+ BisPtr->Shutdown (*BisAppHandle);
+ return NULL;
+ }
+ //
+ // Get BIS check flag.
+ // If the BIS check flag cannot be read, shutdown BIS and return NULL.
+ //
+ DEBUG ((DEBUG_INFO, "\nBisPtr->GetBootObjectAuthorizationCheckFlag() "));
+
+ EfiStatus = BisPtr->GetBootObjectAuthorizationCheckFlag (*BisAppHandle, &BisCheckFlag);
+
+ if (EFI_ERROR (EfiStatus)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxebcBisStart()""\n BisPtr->GetBootObjectAuthorizationCheckFlag() %r (%xh)\n",
+ EfiStatus,
+ EfiStatus)
+ );
+
+ BisPtr->Shutdown (*BisAppHandle);
+ return NULL;
+ }
+ //
+ // If the BIS check flag is FALSE, shutdown BIS and return NULL.
+ //
+ if (!BisCheckFlag) {
+ DEBUG ((DEBUG_INFO, "\nBIS check flag is FALSE.\n"));
+ BisPtr->Shutdown (*BisAppHandle);
+ return NULL;
+ } else {
+ DEBUG ((DEBUG_INFO, "\nBIS check flag is TRUE."));
+ }
+ //
+ // Early out if caller does not want signature information.
+ //
+ if (BisDataSigInfo == NULL) {
+ return BisPtr;
+ }
+ //
+ // Get BIS signature information.
+ // If the signature information cannot be read or is invalid,
+ // shutdown BIS and return NULL.
+ //
+ DEBUG ((DEBUG_INFO, "\nBisPtr->GetSignatureInfo() "));
+
+ EfiStatus = BisPtr->GetSignatureInfo (*BisAppHandle, BisDataSigInfo);
+
+ if (EFI_ERROR (EfiStatus)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxebcBisStart()""\n BisPtr_GetSignatureInfo() %r (%xh)\n",
+ EfiStatus,
+ EfiStatus)
+ );
+
+ BisPtr->Shutdown (*BisAppHandle);
+ return NULL;
+ }
+
+ if (*BisDataSigInfo == NULL) {
+ //
+ // This should never happen.
+ //
+ DEBUG (
+ (DEBUG_NET,
+ "\nPxebcBisStart()""\n BisPtr->GetSignatureInfo() Data pointer is NULL!\n")
+ );
+
+ BisPtr->Shutdown (*BisAppHandle);
+ return NULL;
+ }
+
+ if ((*BisDataSigInfo)->Length < sizeof (EFI_BIS_SIGNATURE_INFO) ||
+ (*BisDataSigInfo)->Length % sizeof (EFI_BIS_SIGNATURE_INFO) ||
+ (*BisDataSigInfo)->Length > sizeof (EFI_BIS_SIGNATURE_INFO) * 63
+ ) {
+ //
+ // This should never happen.
+ //
+ DEBUG (
+ (DEBUG_NET,
+ "\nPxebcBisStart()""\n BisPtr->GetSignatureInfo() Invalid BIS siginfo length.\n")
+ );
+
+ BisPtr->Free (*BisAppHandle, *BisDataSigInfo);
+ BisPtr->Shutdown (*BisAppHandle);
+ return NULL;
+ }
+
+ return BisPtr;
+}
+
+
+/**
+
+
+**/
+VOID
+PxebcBisStop (
+ EFI_BIS_PROTOCOL *BisPtr,
+ BIS_APPLICATION_HANDLE BisAppHandle,
+ EFI_BIS_DATA *BisDataSigInfo
+ )
+{
+ if (BisPtr == NULL) {
+ return ;
+ }
+ //
+ // Free BIS allocated resources and shutdown BIS.
+ // Return TRUE - BIS support is officially detected.
+ //
+ if (BisDataSigInfo != NULL) {
+ BisPtr->Free (BisAppHandle, BisDataSigInfo);
+ }
+
+ BisPtr->Shutdown (BisAppHandle);
+}
+
+
+/**
+
+ @return TRUE := verified
+ @return FALSE := not verified
+
+**/
+BOOLEAN
+PxebcBisVerify (
+ PXE_BASECODE_DEVICE *Private,
+ VOID *FileBuffer,
+ UINTN FileLength,
+ VOID *CredentialBuffer,
+ UINTN CredentialLength
+ )
+{
+ EFI_BIS_PROTOCOL *BisPtr;
+ BIS_APPLICATION_HANDLE BisAppHandle;
+ EFI_BIS_DATA FileData;
+ EFI_BIS_DATA CredentialData;
+ EFI_STATUS EfiStatus;
+ BOOLEAN IsVerified;
+
+ if (Private == NULL || FileBuffer == NULL || FileLength == 0 || CredentialBuffer == NULL || CredentialLength == 0) {
+ return FALSE;
+ }
+
+ BisPtr = PxebcBisStart (Private, &BisAppHandle, NULL);
+
+ if (BisPtr == NULL) {
+ return FALSE;
+ }
+
+ FileData.Length = (UINT32) FileLength;
+ FileData.Data = FileBuffer;
+ CredentialData.Length = (UINT32) CredentialLength;
+ CredentialData.Data = CredentialBuffer;
+
+ EfiStatus = BisPtr->VerifyBootObject (
+ BisAppHandle,
+ &CredentialData,
+ &FileData,
+ &IsVerified
+ );
+
+ PxebcBisStop (BisPtr, BisAppHandle, NULL);
+
+ return (BOOLEAN) ((EFI_ERROR (EfiStatus)) ? FALSE : (IsVerified ? TRUE : FALSE));
+}
+
+
+/**
+
+ @return TRUE := BIS present
+ @return FALSE := BIS not present
+
+**/
+BOOLEAN
+PxebcBisDetect (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ EFI_BIS_PROTOCOL *BisPtr;
+ BIS_APPLICATION_HANDLE BisAppHandle;
+ EFI_BIS_DATA *BisDataSigInfo;
+
+ BisPtr = PxebcBisStart (Private, &BisAppHandle, &BisDataSigInfo);
+
+ if (BisPtr == NULL) {
+ return FALSE;
+ }
+
+ PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo);
+
+ return TRUE;
+}
+
+static VOID *BCNotifyReg;
+
+
+/**
+ Start and initialize the BaseCode protocol, Simple Network protocol and UNDI.
+
+ @param Private Pointer to Pxe BaseCode Protocol
+ @param UseIPv6 Do we want to support IPv6?
+
+ @return EFI_SUCCESS
+ @return EFI_INVALID_PARAMETER
+ @return EFI_UNSUPPORTED
+ @return EFI_ALREADY_STARTED
+ @return EFI_OUT_OF_RESOURCES
+ @return Status is also returned from SNP.Start() and SNP.Initialize().
+
+**/
+EFI_STATUS
+EFIAPI
+BcStart (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN UseIPv6
+ )
+{
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;
+ EFI_SIMPLE_NETWORK_MODE *SnpModePtr;
+ EFI_STATUS Status;
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ //
+ // Lock the instance data
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ //
+ // Make sure BaseCode is not already started.
+ //
+ if (This->Mode->Started) {
+ DEBUG ((DEBUG_WARN, "\nBcStart() BC is already started.\n"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_ALREADY_STARTED;
+ }
+
+#if !SUPPORT_IPV6
+ //
+ // Fail if IPv6 is requested and not supported.
+ //
+ if (UseIPv6) {
+ DEBUG ((DEBUG_WARN, "\nBcStart() IPv6 is not supported.\n"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_UNSUPPORTED;
+ }
+#endif
+ //
+ // Setup shortcuts to SNP protocol and data structure.
+ //
+ SnpPtr = Private->SimpleNetwork;
+ SnpModePtr = SnpPtr->Mode;
+
+ //
+ // Start and initialize SNP.
+ //
+ if (SnpModePtr->State == EfiSimpleNetworkStopped) {
+ StatCode = (*SnpPtr->Start) (SnpPtr);
+
+ if (SnpModePtr->State != EfiSimpleNetworkStarted) {
+ DEBUG ((DEBUG_WARN, "\nBcStart() Could not start SNP.\n"));
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+ }
+ }
+ //
+ // acquire memory for mode and transmit/receive buffers
+ //
+ if (SnpModePtr->State == EfiSimpleNetworkStarted) {
+ StatCode = (*SnpPtr->Initialize) (SnpPtr, 0, 0);
+
+ if (SnpModePtr->State != EfiSimpleNetworkInitialized) {
+ DEBUG ((DEBUG_WARN, "\nBcStart() Could not initialize SNP."));
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+ }
+ }
+ //
+ // Dump debug info.
+ //
+ DEBUG ((DEBUG_INFO, "\nBC Start()"));
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->State %Xh",
+ SnpModePtr->State)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->HwAddressSize %Xh",
+ SnpModePtr->HwAddressSize)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->MediaHeaderSize %Xh",
+ SnpModePtr->MediaHeaderSize)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->MaxPacketSize %Xh",
+ SnpModePtr->MaxPacketSize)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->MacAddressChangeable %Xh",
+ SnpModePtr->MacAddressChangeable)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->MultipleTxSupported %Xh",
+ SnpModePtr->MultipleTxSupported)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->CurrentAddress %Xh",
+ *((UINTN *)&SnpModePtr->CurrentAddress))
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->BroadcastAddress %Xh",
+ *((UINTN *)&SnpModePtr->BroadcastAddress))
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->PermanentAddress %Xh",
+ *((UINTN *)&SnpModePtr->PermanentAddress))
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->NvRamSize %Xh",
+ SnpModePtr->NvRamSize)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->NvRamAccessSize %Xh",
+ SnpModePtr->NvRamAccessSize)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->ReceiveFilterMask %Xh",
+ SnpModePtr->ReceiveFilterMask)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->ReceiveFilterSetting %Xh",
+ SnpModePtr->ReceiveFilterSetting)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->MCastFilterCount %Xh",
+ SnpModePtr->MCastFilterCount)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->MCastFilter %Xh",
+ SnpModePtr->MCastFilter)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->IfType %Xh",
+ SnpModePtr->IfType)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->MediaPresentSupported %Xh",
+ SnpModePtr->MediaPresentSupported)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nSnpModePtr->MediaPresent %Xh",
+ SnpModePtr->MediaPresent)
+ );
+
+ //
+ // If media check is supported and there is no media,
+ // return error to caller.
+ //
+ if (SnpModePtr->MediaPresentSupported && !SnpModePtr->MediaPresent) {
+ DEBUG ((DEBUG_WARN, "\nBcStart() Media not present.\n"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NO_MEDIA;
+ }
+ //
+ // Allocate Tx/Rx buffers
+ //
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ BUFFER_ALLOCATE_SIZE,
+ &Private->TransmitBufferPtr
+ );
+
+ if (!EFI_ERROR (Status)) {
+ ZeroMem (Private->TransmitBufferPtr, BUFFER_ALLOCATE_SIZE);
+ } else {
+ DEBUG ((DEBUG_NET, "\nBcStart() Could not alloc TxBuf.\n"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ BUFFER_ALLOCATE_SIZE,
+ &Private->ReceiveBufferPtr
+ );
+
+ if (!EFI_ERROR (Status)) {
+ ZeroMem (Private->ReceiveBufferPtr, BUFFER_ALLOCATE_SIZE);
+ } else {
+ DEBUG ((DEBUG_NET, "\nBcStart() Could not alloc RxBuf.\n"));
+ gBS->FreePool (Private->TransmitBufferPtr);
+ EfiReleaseLock (&Private->Lock);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ 256,
+ &Private->TftpErrorBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (Private->ReceiveBufferPtr);
+ gBS->FreePool (Private->TransmitBufferPtr);
+ EfiReleaseLock (&Private->Lock);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->AllocatePool (EfiBootServicesData, 256, &Private->TftpAckBuffer);
+
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (Private->TftpErrorBuffer);
+ gBS->FreePool (Private->ReceiveBufferPtr);
+ gBS->FreePool (Private->TransmitBufferPtr);
+ EfiReleaseLock (&Private->Lock);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Initialize private BaseCode instance data
+ //
+ do {
+ Private->RandomPort = (UINT16) (Private->RandomPort + PXE_RND_PORT_LOW + Random (Private));
+ } while (Private->RandomPort < PXE_RND_PORT_LOW);
+
+ Private->Igmpv1TimeoutEvent = NULL;
+ Private->UseIgmpv1Reporting = TRUE;
+ Private->IpLength = IP_ADDRESS_LENGTH (Private->EfiBc.Mode);
+
+ //
+ // Initialize Mode structure
+ //
+ ZeroMem (Private->EfiBc.Mode, sizeof (EFI_PXE_BASE_CODE_MODE));
+ //
+ // check for callback protocol and set boolean
+ //
+ SetMakeCallback (Private);
+ Private->EfiBc.Mode->Started = TRUE;
+ Private->EfiBc.Mode->TTL = DEFAULT_TTL;
+ Private->EfiBc.Mode->ToS = DEFAULT_ToS;
+ Private->EfiBc.Mode->UsingIpv6 = UseIPv6;
+
+ //
+ // Set to PXE_TRUE by the BC constructor if this BC implementation
+ // supports IPv6.
+ //
+ Private->EfiBc.Mode->Ipv6Supported = SUPPORT_IPV6;
+
+ Private->EfiBc.Mode->Ipv6Available = FALSE;
+ //
+ // Set to TRUE by the BC constructor if this BC implementation
+ // supports BIS.
+ //
+ Private->EfiBc.Mode->BisSupported = TRUE;
+ Private->EfiBc.Mode->BisDetected = PxebcBisDetect (Private);
+
+ //
+ // This field is set to PXE_TRUE by the BC Start() function. When this
+ // field is PXE_TRUE, ARP packets are sent as needed to get IP and MAC
+ // addresses. This can cause unexpected delays in the DHCP(), Discover()
+ // and MTFTP() functions. Setting this to PXE_FALSE will cause these
+ // functions to fail if the required IP/MAC information is not in the
+ // ARP cache. The value of this field can be changed by an application
+ // at any time.
+ //
+ Private->EfiBc.Mode->AutoArp = TRUE;
+
+ //
+ // Unlock the instance data
+ //
+ EfiReleaseLock (&Private->Lock);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Stop the BaseCode protocol, Simple Network protocol and UNDI.
+
+ @param Private Pointer to Pxe BaseCode Protocol
+
+ @retval 0 Successfully stopped
+ @retval !0 Failed
+
+**/
+EFI_STATUS
+EFIAPI
+BcStop (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This
+ )
+{
+ //
+ // Lock the instance data
+ //
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;
+ EFI_SIMPLE_NETWORK_MODE *SnpModePtr;
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ PxebcMode = Private->EfiBc.Mode;
+ SnpPtr = Private->SimpleNetwork;
+ SnpModePtr = SnpPtr->Mode;
+
+ //
+ // Issue BC command
+ //
+ StatCode = EFI_NOT_STARTED;
+
+ if (SnpModePtr->State == EfiSimpleNetworkInitialized) {
+ StatCode = (*SnpPtr->Shutdown) (SnpPtr);
+ }
+
+ if (SnpModePtr->State == EfiSimpleNetworkStarted) {
+ StatCode = (*SnpPtr->Stop) (SnpPtr);
+ }
+
+ if (Private->TransmitBufferPtr != NULL) {
+ gBS->FreePool (Private->TransmitBufferPtr);
+ Private->TransmitBufferPtr = NULL;
+ }
+
+ if (Private->ReceiveBufferPtr != NULL) {
+ gBS->FreePool (Private->ReceiveBufferPtr);
+ Private->ReceiveBufferPtr = NULL;
+ }
+
+ if (Private->ArpBuffer != NULL) {
+ gBS->FreePool (Private->ArpBuffer);
+ Private->ArpBuffer = NULL;
+ }
+
+ if (Private->TftpErrorBuffer != NULL) {
+ gBS->FreePool (Private->TftpErrorBuffer);
+ Private->TftpErrorBuffer = NULL;
+ }
+
+ if (Private->TftpAckBuffer != NULL) {
+ gBS->FreePool (Private->TftpAckBuffer);
+ Private->TftpAckBuffer = NULL;
+ }
+
+ if (Private->Igmpv1TimeoutEvent != NULL) {
+ gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
+ Private->Igmpv1TimeoutEvent = NULL;
+ }
+
+ Private->FileSize = 0;
+
+ if (!Private->EfiBc.Mode->Started) {
+ StatCode = EFI_NOT_STARTED;
+ } else {
+ Private->EfiBc.Mode->Started = FALSE;
+ }
+
+ //
+ // Unlock the instance data
+ //
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+
+const IPV4_ADDR AllSystemsGroup = { 224, 0, 0, 1 };
+
+
+/**
+ Set up the IP filter
+
+ @param Private Pointer to Pxe BaseCode Protocol
+ @param Filter Pointer to the filter
+
+ @retval 0 Successfully set the filter
+ @retval !0 Failed
+
+**/
+EFI_STATUS
+IpFilter (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN EFI_PXE_BASE_CODE_IP_FILTER *Filter
+ )
+{
+ EFI_STATUS StatCode;
+ EFI_MAC_ADDRESS MACadds[PXE_IP_FILTER_SIZE];
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;
+ EFI_SIMPLE_NETWORK_MODE *SnpModePtr;
+ UINT32 Enable;
+ UINT32 Disable;
+ UINTN Index;
+ UINTN Index2;
+
+ PxebcMode = Private->EfiBc.Mode;
+ SnpPtr = Private->SimpleNetwork;
+ SnpModePtr = SnpPtr->Mode;
+
+ //
+ // validate input parameters
+ // must have a filter
+ // must not have any extra filter bits set
+ //
+ if (Filter == NULL ||
+ (Filter->Filters &~FILTER_BITS)
+ //
+ // must not have a count which is too large or with no IP list
+ //
+ ||
+ (Filter->IpCnt && (!Filter->IpList || Filter->IpCnt > PXE_IP_FILTER_SIZE))
+ //
+ // must not have incompatible filters - promiscuous incompatible with anything else
+ //
+ ||
+ (
+ (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) &&
+ ((Filter->Filters &~EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) || Filter->IpCnt)
+ )
+ ) {
+ DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #1"));
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // promiscuous multicast incompatible with multicast in IP list
+ //
+ if (Filter->IpCnt && (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST)) {
+ for (Index = 0; Index < Filter->IpCnt; ++Index) {
+ if (IS_MULTICAST (&Filter->IpList[Index])) {
+ DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #2"));
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+ //
+ // leave groups for all those multicast which are no longer enabled
+ //
+ for (Index = 0; Index < PxebcMode->IpFilter.IpCnt; ++Index) {
+ if (!IS_MULTICAST (&PxebcMode->IpFilter.IpList[Index])) {
+ continue;
+ }
+
+ for (Index2 = 0; Index2 < Filter->IpCnt; ++Index2) {
+ if (!CompareMem (&PxebcMode->IpFilter.IpList[Index], &Filter->IpList[Index2], IP_ADDRESS_LENGTH (PxebcMode))) {
+ //
+ // still enabled
+ //
+ break;
+ }
+ }
+ //
+ // if we didn't find it, remove from group
+ //
+ if (Index2 == Filter->IpCnt) {
+ IgmpLeaveGroup (Private, &PxebcMode->IpFilter.IpList[Index]);
+ }
+ }
+ //
+ // set enable bits, convert multicast ip adds, join groups
+ // allways leave receive broadcast enabled at hardware layer
+ //
+ Index2 = 0;
+
+ if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) {
+ Enable = EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+ } else {
+ if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) {
+ Enable = EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+ } else {
+ Enable = EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+
+ for (Index = 0; Index < Filter->IpCnt; ++Index) {
+ PxebcMode->IpFilter.IpList[Index] = Filter->IpList[Index];
+
+ if (IS_MULTICAST (&Filter->IpList[Index])) {
+ EFI_IP_ADDRESS *TmpIp;
+
+ Enable |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+
+ //
+ // if this is the first group, add the all systems group to mcast list
+ //
+ if (!Index2)
+ {
+ TmpIp = (EFI_IP_ADDRESS *) &AllSystemsGroup;
+ --Index;
+ } else {
+ TmpIp = (EFI_IP_ADDRESS *) &Filter->IpList[Index];
+ }
+ //
+ // get MAC address of IP
+ //
+ StatCode = (*SnpPtr->MCastIpToMac) (SnpPtr, PxebcMode->UsingIpv6, TmpIp, &MACadds[Index2++]);
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_INFO,
+ "\nIpFilter() Exit #2 %Xh (%r)",
+ StatCode,
+ StatCode)
+ );
+ return StatCode;
+ }
+ } else {
+ Enable |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+ }
+ }
+ }
+
+ if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) {
+ Enable |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+ }
+ }
+ //
+ // if nothing changed, just return
+ //
+ DEBUG (
+ (DEBUG_INFO,
+ "\nsnp->ReceiveFilterSetting == %Xh Filter->IpCnt == %Xh",
+ SnpModePtr->ReceiveFilterSetting,
+ Filter->IpCnt)
+ );
+
+ if (SnpModePtr->ReceiveFilterSetting == Enable && !Filter->IpCnt) {
+ DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #4"));
+ return EFI_SUCCESS;
+ }
+ //
+ // disable those currently set but not set in new filter
+ //
+ Disable = SnpModePtr->ReceiveFilterSetting &~Enable;
+
+ StatCode = SnpPtr->ReceiveFilters (SnpPtr, Enable, Disable, FALSE, Index2, MACadds);
+
+ PxebcMode->IpFilter.IpCnt = Filter->IpCnt;
+
+ //
+ // join groups for all multicast in list
+ //
+ for (Index = 0; Index < Filter->IpCnt; ++Index) {
+ if (IS_MULTICAST (&Filter->IpList[Index])) {
+ IgmpJoinGroup (Private, &Filter->IpList[Index]);
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #5 %Xh (%r)", StatCode, StatCode));
+
+ return StatCode;
+}
+
+
+/**
+ Call the IP filter
+
+ @param Private Pointer to Pxe BaseCode Protocol
+ @param Filter Pointer to the filter
+
+ @retval 0 Successfully set the filter
+ @retval !0 Failed
+
+**/
+EFI_STATUS
+EFIAPI
+BcIpFilter (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_PXE_BASE_CODE_IP_FILTER *Filter
+ )
+{
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+ UINTN Index;
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < Filter->IpCnt; ++Index) {
+ if ((Filter->IpList[Index].Addr[0]) == BROADCAST_IPv4) {
+ //
+ // The IP is a broadcast address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+
+ if (Filter == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Issue BC command
+ //
+ StatCode = IpFilter (Private, Filter);
+
+ //
+ // Unlock the instance data
+ //
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+
+
+/**
+ Set the Base Code behavior parameters
+
+ @param This Pointer to Pxe BaseCode Protocol
+ @param AutoArpPtr Boolean to do ARP stuff
+ @param SendGuidPtr Boolean whether or not to send GUID info
+ @param TimeToLivePtr Value for Total time to live
+ @param TypeOfServicePtr Value for Type of Service
+ @param MakeCallbackPtr Boolean to determine if we make callbacks
+
+ @retval 0 Successfully set the parameters
+ @retval !0 Failed
+
+**/
+EFI_STATUS
+EFIAPI
+BcSetParameters (
+ EFI_PXE_BASE_CODE_PROTOCOL *This,
+ BOOLEAN *AutoArpPtr,
+ BOOLEAN *SendGuidPtr,
+ UINT8 *TimeToLivePtr,
+ UINT8 *TypeOfServicePtr,
+ BOOLEAN *MakeCallbackPtr
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ EFI_GUID TmpGuid;
+ UINT8 *SerialNumberPtr;
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+
+ DEBUG ((DEBUG_INFO, "\nSetParameters() Entry. "));
+
+ PxebcMode = Private->EfiBc.Mode;
+ StatCode = EFI_SUCCESS;
+
+ if (SendGuidPtr != NULL) {
+ if (*SendGuidPtr) {
+ if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber (&TmpGuid, &SerialNumberPtr) != EFI_SUCCESS) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ if (MakeCallbackPtr != NULL) {
+ if (*MakeCallbackPtr) {
+ if (!SetMakeCallback (Private)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ PxebcMode->MakeCallbacks = *MakeCallbackPtr;
+ }
+
+ if (AutoArpPtr != NULL) {
+ PxebcMode->AutoArp = *AutoArpPtr;
+ }
+
+ if (SendGuidPtr != NULL) {
+ PxebcMode->SendGUID = *SendGuidPtr;
+ }
+
+ if (TimeToLivePtr != NULL) {
+ PxebcMode->TTL = *TimeToLivePtr;
+ }
+
+ if (TypeOfServicePtr != NULL) {
+ PxebcMode->ToS = *TypeOfServicePtr;
+ }
+ //
+ // Unlock the instance data
+ //
+ DEBUG ((DEBUG_INFO, "\nSetparameters() Exit = %xh ", StatCode));
+
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+//
+// //////////////////////////////////////////////////////////
+//
+// BC Set Station IP Routine
+//
+
+/**
+ Set the station IP address
+
+ @param This Pointer to Pxe BaseCode Protocol
+ @param StationIpPtr Pointer to the requested IP address to set in base
+ code
+ @param SubnetMaskPtr Pointer to the requested subnet mask for the base
+ code
+
+ @retval EFI_SUCCESS Successfully set the parameters
+ @retval EFI_NOT_STARTED BC has not started
+
+**/
+EFI_STATUS
+EFIAPI
+BcSetStationIP (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_IP_ADDRESS *StationIpPtr,
+ IN EFI_IP_ADDRESS *SubnetMaskPtr
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+ UINT32 SubnetMask;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ StatCode = EFI_NOT_STARTED;
+ goto RELEASE_LOCK;
+ }
+
+ PxebcMode = Private->EfiBc.Mode;
+
+ if (!Private->GoodStationIp && ((StationIpPtr == NULL) || (SubnetMaskPtr == NULL))) {
+ //
+ // It's not allowed to only set one of the two addresses while there isn't a previous
+ // GOOD address configuration.
+ //
+ StatCode = EFI_INVALID_PARAMETER;
+ goto RELEASE_LOCK;
+ }
+
+ if (SubnetMaskPtr != NULL) {
+ SubnetMask = SubnetMaskPtr->Addr[0];
+
+ if (SubnetMask & (SubnetMask + 1)) {
+ //
+ // the subnet mask is valid if it's with leading continuous 1 bits.
+ //
+ StatCode = EFI_INVALID_PARAMETER;
+ goto RELEASE_LOCK;
+ }
+ } else {
+ SubnetMaskPtr = &PxebcMode->SubnetMask;
+ SubnetMask = SubnetMaskPtr->Addr[0];
+ }
+
+ if (StationIpPtr == NULL) {
+ StationIpPtr = &PxebcMode->StationIp;
+ }
+
+ if (!IS_INADDR_UNICAST (StationIpPtr) ||
+ ((StationIpPtr->Addr[0] | SubnetMask) == BROADCAST_IPv4)) {
+ //
+ // The station IP is not a unicast address.
+ //
+ StatCode = EFI_INVALID_PARAMETER;
+ goto RELEASE_LOCK;
+ }
+
+ PxebcMode->StationIp = *StationIpPtr;
+ PxebcMode->SubnetMask = *SubnetMaskPtr;
+ Private->GoodStationIp = TRUE;
+
+RELEASE_LOCK:
+ //
+ // Unlock the instance data
+ //
+ EfiReleaseLock (&Private->Lock);
+
+ return StatCode;
+}
+
+EFI_DRIVER_BINDING_PROTOCOL mPxeBcDriverBinding = {
+ PxeBcDriverSupported,
+ PxeBcDriverStart,
+ PxeBcDriverStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+
+/**
+ Test to see if this driver supports Controller. Any Controller
+ than contains a Snp protocol can be supported.
+
+ @param This Protocol instance pointer.
+ @param Controller Handle of device to test.
+ @param RemainingDevicePath Not used.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &SnpPtr,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+}
+
+
+/**
+ Start the Base code driver.
+
+ @param This Protocol instance pointer.
+ @param Controller Handle of device to test.
+ @param RemainingDevicePath Not used.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ PXE_BASECODE_DEVICE *Private;
+ LOADFILE_DEVICE *pLF;
+
+ //
+ // Allocate structures needed by BaseCode and LoadFile protocols.
+ //
+ Private = AllocateZeroPool (sizeof (PXE_BASECODE_DEVICE));
+
+ if (Private == NULL ) {
+ DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc PXE_BASECODE_DEVICE structure.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ pLF = AllocateZeroPool (sizeof (LOADFILE_DEVICE));
+ if (pLF == NULL) {
+ DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc LOADFILE_DEVICE structure.\n"));
+ FreePool (Private);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Private->EfiBc.Mode = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_MODE));
+ if (Private->EfiBc.Mode == NULL) {
+ DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc Mode structure.\n"));
+ FreePool (Private);
+ FreePool (pLF);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Lock access, just in case
+ //
+ EfiInitializeLock (&Private->Lock, TPL_CALLBACK);
+ EfiAcquireLock (&Private->Lock);
+
+ EfiInitializeLock (&pLF->Lock, TPL_CALLBACK);
+ EfiAcquireLock (&pLF->Lock);
+
+ //
+ // Initialize PXE structure
+ //
+ //
+ // First initialize the internal 'private' data that the application
+ // does not see.
+ //
+ Private->Signature = PXE_BASECODE_DEVICE_SIGNATURE;
+ Private->Handle = Controller;
+
+ //
+ // Get the NII interface
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &Private->NiiPtr,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid,
+ (VOID **) &Private->NiiPtr,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto PxeBcError;
+ }
+ }
+ //
+ // Get the Snp interface
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &Private->SimpleNetwork,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto PxeBcError;
+ }
+
+ //
+ // Next, initialize the external 'public' data that
+ // the application does see.
+ //
+ Private->EfiBc.Revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION;
+ Private->EfiBc.Start = BcStart;
+ Private->EfiBc.Stop = BcStop;
+ Private->EfiBc.Dhcp = BcDhcp;
+ Private->EfiBc.Discover = BcDiscover;
+ Private->EfiBc.Mtftp = BcMtftp;
+ Private->EfiBc.UdpWrite = BcUdpWrite;
+ Private->EfiBc.UdpRead = BcUdpRead;
+ Private->EfiBc.Arp = BcArp;
+ Private->EfiBc.SetIpFilter = BcIpFilter;
+ Private->EfiBc.SetParameters = BcSetParameters;
+ Private->EfiBc.SetStationIp = BcSetStationIP;
+ Private->EfiBc.SetPackets = BcSetPackets;
+
+ //
+ // Initialize BaseCode Mode structure
+ //
+ Private->EfiBc.Mode->Started = FALSE;
+ Private->EfiBc.Mode->TTL = DEFAULT_TTL;
+ Private->EfiBc.Mode->ToS = DEFAULT_ToS;
+ Private->EfiBc.Mode->UsingIpv6 = FALSE;
+ Private->EfiBc.Mode->AutoArp = TRUE;
+
+ //
+ // Set to PXE_TRUE by the BC constructor if this BC
+ // implementation supports IPv6.
+ //
+ Private->EfiBc.Mode->Ipv6Supported = SUPPORT_IPV6;
+
+#if SUPPORT_IPV6
+ Private->EfiBc.Mode->Ipv6Available = Private->NiiPtr->Ipv6Supported;
+#else
+ Private->EfiBc.Mode->Ipv6Available = FALSE;
+#endif
+ //
+ // Set to TRUE by the BC constructor if this BC
+ // implementation supports BIS.
+ //
+ Private->EfiBc.Mode->BisSupported = TRUE;
+ Private->EfiBc.Mode->BisDetected = PxebcBisDetect (Private);
+
+ //
+ // Initialize LoadFile structure.
+ //
+ pLF->Signature = LOADFILE_DEVICE_SIGNATURE;
+ pLF->LoadFile.LoadFile = LoadFile;
+ pLF->Private = Private;
+
+ //
+ // Install protocol interfaces.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiPxeBaseCodeProtocolGuid,
+ &Private->EfiBc,
+ &gEfiLoadFileProtocolGuid,
+ &pLF->LoadFile,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ goto PxeBcError;
+ }
+ //
+ // Release locks.
+ //
+ EfiReleaseLock (&pLF->Lock);
+ EfiReleaseLock (&Private->Lock);
+ return Status;
+
+PxeBcError: ;
+ gBS->FreePool (Private->EfiBc.Mode);
+ gBS->FreePool (Private);
+ gBS->FreePool (pLF);
+ return Status;
+}
+
+
+/**
+ Stop the Base code driver.
+
+ @param This Protocol instance pointer.
+ @param Controller Handle of device to test.
+ @param NumberOfChildren Not used
+ @param ChildHandleBuffer Not used
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOAD_FILE_PROTOCOL *LfProtocol;
+ LOADFILE_DEVICE *LoadDevice;
+
+ //
+ // Get our context back.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiLoadFileProtocolGuid,
+ (VOID **) &LfProtocol,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ LoadDevice = EFI_LOAD_FILE_DEV_FROM_THIS (LfProtocol);
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gEfiLoadFileProtocolGuid,
+ &LoadDevice->LoadFile,
+ &gEfiPxeBaseCodeProtocolGuid,
+ &LoadDevice->Private->EfiBc,
+ NULL
+ );
+
+ if (!EFI_ERROR (Status)) {
+
+ Status = gBS->CloseProtocol (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->FreePool (LoadDevice->Private->EfiBc.Mode);
+ gBS->FreePool (LoadDevice->Private);
+ gBS->FreePool (LoadDevice);
+ }
+
+ return Status;
+}
+
+
+/**
+ Initialize the base code drivers and install the driver binding
+
+ Standard EFI Image Entry
+
+ @retval EFI_SUCCESS This driver was successfully bound
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeBCDriver (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Initialize EFI library
+ //
+ Status = EfiLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &mPxeBcDriverBinding,
+ NULL,
+ COMPONENT_NAME,
+ NULL,
+ NULL
+ );
+
+ InitArpHeader ();
+ OptionsStrucInit ();
+
+ return EFI_SUCCESS;
+}
+
+/* eof - bc.c */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h
new file mode 100644
index 0000000000..71d1518656
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h
@@ -0,0 +1,564 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ bc.h
+
+Abstract:
+
+
+**/
+
+#ifndef _BC_H
+#define _BC_H
+
+#include <PiDxe.h>
+
+#include <Guid/SmBios.h>
+#include <Protocol/Bis.h>
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/PxeBaseCodeCallBack.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/Tcp.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+
+#define CALLBACK_INTERVAL 100 // ten times a second
+#define FILTER_BITS (EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP | \
+ EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST | \
+ EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS | \
+ EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST \
+ )
+
+#define WAIT_TX_TIMEOUT 1000
+
+#define SUPPORT_IPV6 0
+
+#define PXE_BASECODE_DEVICE_SIGNATURE 'pxed'
+
+//
+// Determine the classes of IPv4 address
+//
+#define IS_CLASSA_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0x80) == 0x00)
+#define IS_CLASSB_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0xc0) == 0x80)
+#define IS_CLASSC_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0xe0) == 0xc0)
+#define IS_INADDR_UNICAST(x) ((IS_CLASSA_IPADDR(x) || IS_CLASSB_IPADDR(x) || IS_CLASSC_IPADDR(x)) && (((EFI_IP_ADDRESS*)x)->Addr[0] != 0) )
+
+//
+// Definitions for internet group management protocol version 2 message
+// structure
+// Per RFC 2236, November 1997
+//
+#pragma pack(1)
+
+typedef struct {
+ UINT8 Type;
+ UINT8 MaxRespTime; // in tenths of a second
+ UINT16 Checksum; // ones complement of ones complement sum of
+ // 16 bit words of message
+ UINT32 GroupAddress; // for general query, all systems group,
+ // for group specific, the group
+} IGMPV2_MESSAGE;
+
+#define IGMP_TYPE_QUERY 0x11
+#define IGMP_TYPE_REPORT 0x16
+#define IGMP_TYPE_V1REPORT 0x12
+#define IGMP_TYPE_LEAVE_GROUP 0x17
+
+#define IGMP_DEFAULT_MAX_RESPONSE_TIME 10 // 10 second default
+#pragma pack()
+
+#define MAX_MCAST_GROUPS 8 // most we allow ourselves to join at once
+#define MAX_OFFERS 16
+
+typedef struct {
+ UINTN Signature;
+ EFI_LOCK Lock;
+ BOOLEAN ShowErrorMessages;
+ EFI_TCP_PROTOCOL Tcp;
+ EFI_PXE_BASE_CODE_PROTOCOL EfiBc;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *CallbackProtocolPtr;
+ EFI_HANDLE Handle;
+
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiPtr;
+ EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetwork;
+ UINT8 *TransmitBufferPtr;
+ UINT8 *ReceiveBufferPtr;
+ EFI_PXE_BASE_CODE_FUNCTION Function;
+
+ UINTN OldestArpEntry;
+ UINTN MCastGroupCount;
+ EFI_EVENT Igmpv1TimeoutEvent;
+ BOOLEAN UseIgmpv1Reporting;
+ EFI_EVENT IgmpGroupEvent[MAX_MCAST_GROUPS];
+ UINT16 RandomPort;
+
+#if SUPPORT_IPV6
+ //
+ // TBD
+ //
+#else
+ UINT32 MCastGroup[MAX_MCAST_GROUPS];
+#endif
+
+ BOOLEAN GoodStationIp;
+ BOOLEAN DidTransmit;
+ UINTN IpLength;
+ VOID *DhcpPacketBuffer;
+ UINTN FileSize;
+ VOID *BootServerReceiveBuffer;
+ EFI_IP_ADDRESS ServerIp;
+
+ //
+ // work area
+ // for dhcp
+ //
+ VOID *ReceiveBuffers;
+ VOID *TransmitBuffer;
+ UINTN NumOffersReceived;
+ UINT16 TotalSeconds;
+
+ //
+ // arrays for different types of offers
+ //
+ UINT8 ServerCount[4];
+ UINT8 OfferCount[4][MAX_OFFERS];
+ UINT8 GotBootp;
+ UINT8 GotProxy[4];
+ UINT8 BinlProxies[MAX_OFFERS];
+
+ UINT8 *ArpBuffer;
+ UINT8 *TftpAckBuffer;
+ UINT8 *TftpErrorBuffer;
+ IGMPV2_MESSAGE IgmpMessage;
+ BOOLEAN BigBlkNumFlag;
+ UINT8 Timeout;
+ UINT16 RandomSeed;
+} PXE_BASECODE_DEVICE;
+
+//
+// type index
+//
+#define DHCP_ONLY_IX 0
+#define PXE10_IX 1
+#define WfM11a_IX 2
+#define BINL_IX 3
+
+#define PXE_RND_PORT_LOW 2070
+
+//
+//
+//
+#define LOADFILE_DEVICE_SIGNATURE 'pxel'
+
+typedef struct {
+ UINTN Signature;
+ EFI_LOCK Lock;
+ EFI_LOAD_FILE_PROTOCOL LoadFile;
+ PXE_BASECODE_DEVICE *Private;
+} LOADFILE_DEVICE;
+
+#define EFI_BASE_CODE_DEV_FROM_THIS(a) CR (a, PXE_BASECODE_DEVICE, efi_bc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+#define EFI_BASE_CODE_DEV_FROM_TCP(a) CR (a, PXE_BASECODE_DEVICE, Tcp, PXE_BASECODE_DEVICE_SIGNATURE);
+
+#define EFI_LOAD_FILE_DEV_FROM_THIS(a) CR (a, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE)
+
+EFI_BIS_PROTOCOL *
+PxebcBisStart (
+ PXE_BASECODE_DEVICE *Private,
+ BIS_APPLICATION_HANDLE *BisAppHandle,
+ EFI_BIS_DATA **BisDataSigInfo
+ )
+;
+
+VOID
+PxebcBisStop (
+ EFI_BIS_PROTOCOL *Bis,
+ BIS_APPLICATION_HANDLE BisAppHandle,
+ EFI_BIS_DATA *BisDataSigInfo
+ )
+;
+
+BOOLEAN
+PxebcBisVerify (
+ PXE_BASECODE_DEVICE *Private,
+ VOID *FileBuffer,
+ UINTN FileBufferLength,
+ VOID *CredentialBuffer,
+ UINTN CredentialBufferLength
+ )
+;
+
+BOOLEAN
+PxebcBisDetect (
+ PXE_BASECODE_DEVICE *Private
+ )
+;
+
+//
+// Global Variables
+//
+extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName;
+
+//
+// //////////////////////////////////////////////////////////
+//
+// prototypes
+//
+
+/**
+ Initialize the base code drivers and install the driver binding
+
+ Standard EFI Image Entry
+
+ @retval EFI_SUCCESS This driver was successfully bound
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeBCDriver (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcStart (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN UseIpv6
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcStop (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcDhcp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN SortOffers
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcDiscover (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_PXE_BASE_CODE_DISCOVER_INFO * Info OPTIONAL
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcMtftp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,
+ IN OUT VOID *BufferPtr,
+ IN BOOLEAN Overwrite,
+ IN OUT UINT64 *BufferSize,
+ IN UINTN *BlockSize OPTIONAL,
+ IN EFI_IP_ADDRESS * ServerIp,
+ IN UINT8 *Filename,
+ IN EFI_PXE_BASE_CODE_MTFTP_INFO * Info OPTIONAL,
+ IN BOOLEAN DontUseBuffer
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcUdpWrite (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort,
+ IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL
+ IN EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcUdpRead (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL
+ IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN OUT UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcTcpWrite (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN UINT16 *UrgentPointer,
+ IN UINT32 *SequenceNumber,
+ IN UINT32 *AckNumber,
+ IN UINT16 *HlenResCode,
+ IN UINT16 *Window,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN EFI_PXE_BASE_CODE_TCP_PORT *DestPort,
+ IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL
+ IN EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_TCP_PORT *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcTcpRead (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_TCP_PORT *DestPort, OPTIONAL
+ IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_TCP_PORT *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN OUT UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcArp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN EFI_IP_ADDRESS * IpAddr,
+ IN EFI_MAC_ADDRESS * MacAddr OPTIONAL
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcIpFilter (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcSetParameters (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN BOOLEAN *NewAutoArp, OPTIONAL
+ IN BOOLEAN *NewSendGUID, OPTIONAL
+ IN UINT8 *NewTTL, OPTIONAL
+ IN UINT8 *NewToS, OPTIONAL
+ IN BOOLEAN *NewMakeCallback OPTIONAL
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcSetStationIP (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN EFI_IP_ADDRESS * NewStationIp, OPTIONAL
+ IN EFI_IP_ADDRESS * NewSubnetMask OPTIONAL
+ )
+;
+
+EFI_STATUS
+EFIAPI
+BcSetPackets (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ BOOLEAN *NewDhcpDiscoverValid, OPTIONAL
+ BOOLEAN *NewDhcpAckReceived, OPTIONAL
+ BOOLEAN *NewProxyOfferReceived, OPTIONAL
+ BOOLEAN *NewPxeDiscoverValid, OPTIONAL
+ BOOLEAN *NewPxeReplyReceived, OPTIONAL
+ BOOLEAN *NewPxeBisReplyReceived, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewDhcpDiscover, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewDhcpAck, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewProxyOffer, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeDiscover, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeReply, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeBisReply OPTIONAL
+ )
+;
+
+EFI_STATUS
+EFIAPI
+LoadFile (
+ IN EFI_LOAD_FILE_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+;
+
+EFI_STATUS
+PxeBcLibGetSmbiosSystemGuidAndSerialNumber (
+ IN EFI_GUID *SystemGuid,
+ OUT CHAR8 **SystemSerialNumber
+ )
+;
+
+#ifdef EFI_SIZE_REDUCTION_APPLIED
+ #define COMPONENT_NAME_CODE(code)
+ #define COMPONENT_NAME NULL
+#else
+ #define COMPONENT_NAME_CODE(code) code
+ #define COMPONENT_NAME &gPxeBcComponentName
+#endif
+
+
+//
+// Define SMBIOS tables.
+//
+#pragma pack(1)
+typedef struct {
+ UINT8 AnchorString[4];
+ UINT8 EntryPointStructureChecksum;
+ UINT8 EntryPointLength;
+ UINT8 MajorVersion;
+ UINT8 MinorVersion;
+ UINT16 MaxStructureSize;
+ UINT8 EntryPointRevision;
+ UINT8 FormattedArea[5];
+ UINT8 IntermediateAnchorString[5];
+ UINT8 IntermediateChecksum;
+ UINT16 TableLength;
+ UINT32 TableAddress;
+ UINT16 NumberOfSmbiosStructures;
+ UINT8 SmbiosBcdRevision;
+} SMBIOS_STRUCTURE_TABLE;
+
+//
+// Please note that SMBIOS structures can be odd byte aligned since the
+// unformated section of each record is a set of arbitrary size strings.
+//
+typedef struct {
+ UINT8 Type;
+ UINT8 Length;
+ UINT8 Handle[2];
+} SMBIOS_HEADER;
+
+typedef UINT8 SMBIOS_STRING;
+
+typedef struct {
+ SMBIOS_HEADER Hdr;
+ SMBIOS_STRING Vendor;
+ SMBIOS_STRING BiosVersion;
+ UINT8 BiosSegment[2];
+ SMBIOS_STRING BiosReleaseDate;
+ UINT8 BiosSize;
+ UINT8 BiosCharacteristics[8];
+} SMBIOS_TYPE0;
+
+typedef struct {
+ SMBIOS_HEADER Hdr;
+ SMBIOS_STRING Manufacturer;
+ SMBIOS_STRING ProductName;
+ SMBIOS_STRING Version;
+ SMBIOS_STRING SerialNumber;
+
+ //
+ // always byte copy this data to prevent alignment faults!
+ //
+ EFI_GUID Uuid;
+
+ UINT8 WakeUpType;
+} SMBIOS_TYPE1;
+
+typedef struct {
+ SMBIOS_HEADER Hdr;
+ SMBIOS_STRING Manufacturer;
+ SMBIOS_STRING ProductName;
+ SMBIOS_STRING Version;
+ SMBIOS_STRING SerialNumber;
+} SMBIOS_TYPE2;
+
+typedef struct {
+ SMBIOS_HEADER Hdr;
+ SMBIOS_STRING Manufacturer;
+ UINT8 Type;
+ SMBIOS_STRING Version;
+ SMBIOS_STRING SerialNumber;
+ SMBIOS_STRING AssetTag;
+ UINT8 BootupState;
+ UINT8 PowerSupplyState;
+ UINT8 ThermalState;
+ UINT8 SecurityStatus;
+ UINT8 OemDefined[4];
+} SMBIOS_TYPE3;
+
+typedef struct {
+ SMBIOS_HEADER Hdr;
+ UINT8 Socket;
+ UINT8 ProcessorType;
+ UINT8 ProcessorFamily;
+ SMBIOS_STRING ProcessorManufacture;
+ UINT8 ProcessorId[8];
+ SMBIOS_STRING ProcessorVersion;
+ UINT8 Voltage;
+ UINT8 ExternalClock[2];
+ UINT8 MaxSpeed[2];
+ UINT8 CurrentSpeed[2];
+ UINT8 Status;
+ UINT8 ProcessorUpgrade;
+ UINT8 L1CacheHandle[2];
+ UINT8 L2CacheHandle[2];
+ UINT8 L3CacheHandle[2];
+} SMBIOS_TYPE4;
+
+typedef union {
+ SMBIOS_HEADER *Hdr;
+ SMBIOS_TYPE0 *Type0;
+ SMBIOS_TYPE1 *Type1;
+ SMBIOS_TYPE2 *Type2;
+ SMBIOS_TYPE3 *Type3;
+ SMBIOS_TYPE4 *Type4;
+ UINT8 *Raw;
+} SMBIOS_STRUCTURE_POINTER;
+#pragma pack()
+
+#include "ip.h"
+#include "dhcp.h"
+#include "tftp.h"
+
+#endif /* _BC_H */
+
+/* EOF - bc.h */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c b/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c
new file mode 100644
index 0000000000..18dca38408
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c
@@ -0,0 +1,160 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+#include "Bc.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+PxeBcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+PxeBcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = {
+ PxeBcComponentNameGetDriverName,
+ PxeBcComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = {
+ {
+ "eng",
+ L"PXE Base Code Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+PxeBcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gPxeBcComponentName.SupportedLanguages,
+ mPxeBcDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+PxeBcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h
new file mode 100644
index 0000000000..36f71f775a
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h
@@ -0,0 +1,632 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+**/
+
+#ifndef _DHCP_H
+#define _DHCP_H
+
+//
+// Definitions for DHCP version 4 UDP packet.
+// The field names in this structure are defined and described in RFC 2131.
+//
+#pragma pack(1)
+
+typedef struct {
+ UINT8 op;
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+ UINT8 htype;
+ UINT8 hlen;
+ UINT8 hops;
+ UINT32 xid;
+ UINT16 secs;
+ UINT16 flags;
+#define DHCP_BROADCAST_FLAG 0x8000
+
+ UINT32 ciaddr;
+ UINT32 yiaddr;
+ UINT32 siaddr;
+ UINT32 giaddr;
+ UINT8 chaddr[16];
+ UINT8 sname[64];
+ UINT8 file[128];
+ UINT8 options[312];
+#define OP_PAD 0
+#define OP_END 255
+#define OP_SUBNET_MASK 1
+#define OP_TIME_OFFSET 2
+#define OP_ROUTER_LIST 3
+#define OP_TIME_SERVERS 4
+#define OP_NAME_SERVERS 5
+#define OP_DNS_SERVERS 6
+#define OP_LOG_SERVERS 7
+#define OP_COOKIE_SERVERS 8
+#define OP_LPR_SREVERS 9
+#define OP_IMPRESS_SERVERS 10
+#define OP_RES_LOC_SERVERS 11
+#define OP_HOST_NAME 12
+#define OP_BOOT_FILE_SZ 13
+#define OP_DUMP_FILE 14
+#define OP_DOMAIN_NAME 15
+#define OP_SWAP_SERVER 16
+#define OP_ROOT_PATH 17
+#define OP_EXTENSION_PATH 18
+#define OP_IP_FORWARDING 19
+#define OP_NON_LOCAL_SRC_RTE 20
+#define OP_POLICY_FILTER 21
+#define OP_MAX_DATAGRAM_SZ 22
+#define OP_DEFAULT_TTL 23
+#define OP_MTU_AGING_TIMEOUT 24
+#define OP_MTU_SIZES 25
+#define OP_MTU_TO_USE 26
+#define OP_ALL_SUBNETS_LOCAL 27
+#define OP_BROADCAST_ADD 28
+#define OP_PERFORM_MASK_DISCOVERY 29
+#define OP_RESPOND_TO_MASK_REQ 30
+#define OP_PERFORM_ROUTER_DISCOVERY 31
+#define OP_ROUTER_SOLICIT_ADDRESS 32
+#define OP_STATIC_ROUTER_LIST 33
+#define OP_USE_ARP_TRAILERS 34
+#define OP_ARP_CACHE_TIMEOUT 35
+#define OP_ETHERNET_ENCAPSULATION 36
+#define OP_TCP_DEFAULT_TTL 37
+#define OP_TCP_KEEP_ALIVE_INT 38
+#define OP_KEEP_ALIVE_GARBAGE 39
+#define OP_NIS_DOMAIN_NAME 40
+#define OP_NIS_SERVERS 41
+#define OP_NTP_SERVERS 42
+#define OP_VENDOR_SPECIFIC 43
+#define VEND_PXE_MTFTP_IP 1
+#define VEND_PXE_MTFTP_CPORT 2
+#define VEND_PXE_MTFTP_SPORT 3
+#define VEND_PXE_MTFTP_TMOUT 4
+#define VEND_PXE_MTFTP_DELAY 5
+#define VEND_PXE_DISCOVERY_CONTROL 6
+#define PXE_DISABLE_BROADCAST_DISCOVERY (1 << 0)
+#define PXE_DISABLE_MULTICAST_DISCOVERY (1 << 1)
+#define PXE_ACCEPT_ONLY_PXE_BOOT_SERVERS (1 << 2)
+#define PXE_DO_NOT_PROMPT (1 << 3)
+#define VEND_PXE_DISCOVERY_MCAST_ADDR 7
+#define VEND_PXE_BOOT_SERVERS 8
+#define VEND_PXE_BOOT_MENU 9
+#define VEND_PXE_BOOT_PROMPT 10
+#define VEND_PXE_MCAST_ADDRS_ALLOC 11
+#define VEND_PXE_CREDENTIAL_TYPES 12
+#define VEND_PXE_BOOT_ITEM 71
+#define OP_NBNS_SERVERS 44
+#define OP_NBDD_SERVERS 45
+#define OP_NETBIOS_NODE_TYPE 46
+#define OP_NETBIOS_SCOPE 47
+#define OP_XWINDOW_SYSTEM_FONT_SERVERS 48
+#define OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49
+#define OP_DHCP_REQ_IP_ADD 50
+#define OP_DHCP_LEASE_TIME 51
+#define OP_DHCP_OPTION_OVERLOAD 52
+#define OVLD_FILE 1
+#define OVLD_SRVR_NAME 2
+#define OP_DHCP_MESSAGE_TYPE 53
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+#define OP_DHCP_SERVER_IP 54
+#define OP_DHCP_PARM_REQ_LIST 55
+#define OP_DHCP_ERROR_MESSAGE 56
+#define OP_DHCP_MAX_MESSAGE_SZ 57
+#define OP_DHCP_RENEWAL_TIME 58
+#define OP_DHCP_REBINDING_TIME 59
+#define OP_DHCP_CLASS_IDENTIFIER 60
+#define OP_DHCP_CLIENT_IDENTIFIER 61
+#define OP_NISPLUS_DOMAIN_NAME 64
+#define OP_NISPLUS_SERVERS 65
+#define OP_DHCP_TFTP_SERVER_NAME 66
+#define OP_DHCP_BOOTFILE 67
+#define OP_MOBILE_IP_HOME_AGENTS 68
+#define OP_SMPT_SERVERS 69
+#define OP_POP3_SERVERS 70
+#define OP_NNTP_SERVERS 71
+#define OP_WWW_SERVERS 72
+#define OP_FINGER_SERVERS 73
+#define OP_IRC_SERVERS 74
+#define OP_STREET_TALK_SERVERS 75
+#define OP_STREET_TALK_DIR_ASSIST_SERVERS 76
+#define OP_NDS_SERVERS 85
+#define OP_NDS_TREE_NAME 86
+#define OP_NDS_CONTEXT 87
+#define OP_DHCP_SYSTEM_ARCH 93
+#define OP_DHCP_NETWORK_ARCH 94
+#define OP_DHCP_PLATFORM_ID 97
+} DHCPV4_STRUCT;
+
+//
+// DHCPv4 option header
+//
+typedef struct {
+ UINT8 OpCode;
+ UINT8 Length;
+ //
+ // followed by Data[]
+ //
+} DHCPV4_OP_HEADER;
+
+//
+// Generic DHCPv4 option (header followed by data)
+//
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 Data[1];
+} DHCPV4_OP_STRUCT;
+
+//
+// Maximum DHCP packet size on ethernet
+//
+#define MAX_DHCP_MSG_SZ (MAX_ENET_DATA_SIZE - sizeof (IPV4_HEADER) - sizeof (UDPV4_HEADER))
+
+//
+// Macros used in pxe_bc_dhcp.c and pxe_loadfile.c
+//
+#define DHCPV4_TRANSMIT_BUFFER (*(DHCPV4_STRUCT *) (Private->TransmitBuffer))
+#define DHCPV4_OPTIONS_BUFFER (*(struct optionsstr *) DHCPV4_TRANSMIT_BUFFER.options)
+
+#define DHCPV4_ACK_INDEX 0
+#define PXE_BINL_INDEX 1
+#define PXE_OFFER_INDEX 1
+#define PXE_ACK_INDEX 2
+#define PXE_BIS_INDEX 3
+
+#define DHCPV4_ACK_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[DHCPV4_ACK_INDEX]
+#define PXE_BINL_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_BINL_INDEX]
+#define PXE_OFFER_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_OFFER_INDEX]
+#define PXE_ACK_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_ACK_INDEX]
+#define PXE_BIS_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_BIS_INDEX]
+
+#define DHCPV4_ACK_PACKET DHCPV4_ACK_BUFFER.u.Dhcpv4
+#define PXE_BINL_PACKET PXE_BINL_BUFFER.u.Dhcpv4
+#define PXE_OFFER_PACKET PXE_OFFER_BUFFER.u.Dhcpv4
+#define PXE_ACK_PACKET PXE_ACK_BUFFER.u.Dhcpv4
+#define PXE_BIS_PACKET PXE_BIS_BUFFER.u.Dhcpv4
+
+//
+// network structure definitions
+//
+//
+// some option definitions
+//
+#define DHCPV4_OPTION_LENGTH(type) (sizeof (type) - sizeof (DHCPV4_OP_HEADER))
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 Type;
+} DHCPV4_OP_MESSAGE_TYPE;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 Overload;
+} DHCPV4_OP_OVERLOAD;
+
+//
+// boot server list structure
+// one or more contained in a pxe boot servers structure
+//
+typedef struct {
+ UINT8 IpCount;
+ EFI_IPv4_ADDRESS IpList[1]; // IP count of IPs
+} PXEV4_SERVER_LIST;
+
+typedef struct {
+ UINT8 IpCount;
+ EFI_IPv6_ADDRESS IpList[1]; // IP count of IPs
+} PXEV6_SERVER_LIST;
+
+typedef union {
+ PXEV4_SERVER_LIST Ipv4List;
+ PXEV6_SERVER_LIST Ipv6List;
+} PXE_SERVER_LISTS;
+
+typedef struct {
+ UINT16 Type;
+ PXE_SERVER_LISTS u;
+} PXE_SERVER_LIST;
+
+//
+// pxe boot servers structure
+//
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ PXE_SERVER_LIST ServerList[1]; // one or more
+} PXE_OP_SERVER_LIST;
+
+//
+// pxe boot item structure
+//
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT16 Type;
+ UINT16 Layer;
+} PXE_OP_BOOT_ITEM;
+
+//
+// pxe boot menu item structure
+//
+typedef struct {
+ UINT16 Type;
+ UINT8 DataLen;
+ UINT8 Data[1];
+} PXE_BOOT_MENU_ENTRY;
+
+//
+// pxe boot menu structure
+//
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ PXE_BOOT_MENU_ENTRY MenuItem[1];
+} PXE_OP_BOOT_MENU;
+
+//
+// pxe boot prompt structure
+//
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 Timeout;
+ UINT8 Prompt[1];
+} PXE_OP_BOOT_PROMPT;
+
+#define PXE_BOOT_PROMPT_AUTO_SELECT 0
+#define PXE_BOOT_PROMPT_NO_TIMEOUT 255
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 Class[1];
+} DHCPV4_OP_CLASS;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 File[1];
+} DHCPV4_OP_BOOTFILE;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 VendorOptions[1];
+} DHCPV4_OP_VENDOR_OPTIONS;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 MaxSize[2];
+} DHCPV4_OP_MAX_MESSAGE_SIZE;
+
+typedef struct {
+ UINT8 _OP_SUBNET_MASK; /* 1 */
+ UINT8 _OP_TIME_OFFSET; /* 2 */
+ UINT8 _OP_ROUTER_LIST; /* 3 */
+ UINT8 _OP_TIME_SERVERS; /* 4 */
+ UINT8 _OP_NAME_SERVERS; /* 5 */
+ UINT8 _OP_DNS_SERVERS; /* 6 */
+ UINT8 _OP_HOST_NAME; /* 12 */
+ UINT8 _OP_BOOT_FILE_SZ; /* 13 */
+ UINT8 _OP_DOMAIN_NAME; /* 15 */
+ UINT8 _OP_ROOT_PATH; /* 17 */
+ UINT8 _OP_EXTENSION_PATH; /* 18 */
+ UINT8 _OP_MAX_DATAGRAM_SZ; /* 22 */
+ UINT8 _OP_DEFAULT_TTL; /* 23 */
+ UINT8 _OP_BROADCAST_ADD; /* 28 */
+ UINT8 _OP_NIS_DOMAIN_NAME; /* 40 */
+ UINT8 _OP_NIS_SERVERS; /* 41 */
+ UINT8 _OP_NTP_SERVERS; /* 42 */
+ UINT8 _OP_VENDOR_SPECIFIC; /* 43 */
+ UINT8 _OP_DHCP_REQ_IP_ADD; /* 50 */
+ UINT8 _OP_DHCP_LEASE_TIME; /* 51 */
+ UINT8 _OP_DHCP_SERVER_IP; /* 54 */
+ UINT8 _OP_DHCP_RENEWAL_TIME; /* 58 */
+ UINT8 _OP_DHCP_REBINDING_TIME; /* 59 */
+ UINT8 _OP_DHCP_CLASS_IDENTIFIER; /* 60 */
+ UINT8 _OP_DHCP_TFTP_SERVER_NAME; /* 66 */
+ UINT8 _OP_DHCP_BOOTFILE; /* 67 */
+ UINT8 _OP_DHCP_PLATFORM_ID; /* 97 */
+ UINT8 VendorOption128; // vendor option 128
+ UINT8 VendorOption129; // vendor option 129
+ UINT8 VendorOption130; // vendor option 130
+ UINT8 VendorOption131; // vendor option 131
+ UINT8 VendorOption132; // vendor option 132
+ UINT8 VendorOption133; // vendor option 133
+ UINT8 VendorOption134; // vendor option 134
+ UINT8 VendorOption135; // vendor option 135
+} DHCPV4_REQUESTED_OPTIONS_DATA;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ DHCPV4_REQUESTED_OPTIONS_DATA Data;
+} DHCPV4_OP_REQUESTED_OPTIONS;
+
+typedef struct opipstr {
+ DHCPV4_OP_HEADER Header;
+ EFI_IPv4_ADDRESS Ip;
+} DHCPV4_OP_IP_ADDRESS;
+
+//
+// ip list structure - e.g. router list
+//
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ EFI_IPv4_ADDRESS IpList[1];
+} DHCPV4_OP_IP_LIST;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 Type;
+ UINT8 Guid[sizeof (EFI_GUID)];
+} DHCPV4_OP_CLIENT_ID;
+
+//
+// special options start - someday obsolete ???
+//
+#define DHCPV4_OP_PLATFORM_ID DHCPV4_OP_CLIENT_ID
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT8 Type; // SNP = 2
+ UINT8 MajorVersion;
+ UINT8 MinorVersion;
+} DHCPV4_OP_NETWORK_INTERFACE;
+
+#define UNDI_TYPE 1
+#define SNP_TYPE 2
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ UINT16 Type;
+} DHCPV4_OP_ARCHITECTURE_TYPE;
+//
+// special options end - someday obsolete ???
+//
+typedef struct {
+ UINT8 ClassIdentifier[10]; // PXEClient:
+ UINT8 Lit2[5]; // Arch:
+ UINT8 ArchitectureType[5]; // 00000 - 65536
+ UINT8 Lit3[1]; // :
+ UINT8 InterfaceName[4]; // e.g. UNDI
+ UINT8 Lit4[1]; // :
+ UINT8 UndiMajor[3]; // 000 - 255
+ UINT8 UndiMinor[3]; // 000 - 255
+} DHCPV4_CLASS_ID_DATA;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ DHCPV4_CLASS_ID_DATA Data;
+} DHCPV4_OP_CLASS_ID;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ EFI_IPv4_ADDRESS Ip;
+} DHCPV4_OP_REQUESTED_IP;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ EFI_IPv4_ADDRESS Ip;
+} DHCPV4_OP_SERVER_IP;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ EFI_IPv4_ADDRESS Ip;
+} DHCPV4_OP_SUBNET_MASK;
+
+typedef struct { // oppxedisctlstr {
+ DHCPV4_OP_HEADER Header;
+ UINT8 ControlBits;
+} PXE_OP_DISCOVERY_CONTROL;
+
+#define DISABLE_BCAST (1 << 0)
+#define DISABLE_MCAST (1 << 1)
+#define USE_ACCEPT_LIST (1 << 2)
+#define USE_BOOTFILE (1 << 3)
+
+#pragma pack()
+//
+// definitions of indices to populate option interest array
+//
+#define VEND_PXE_MTFTP_IP_IX 1 // multicast IP address of bootfile for MTFTP listen
+#define VEND_PXE_MTFTP_CPORT_IX 2 // UDP Port to monitor for MTFTP responses - Intel order
+#define VEND_PXE_MTFTP_SPORT_IX 3 // Server UDP Port for MTFTP open - Intel order
+#define VEND_PXE_MTFTP_TMOUT_IX 4 // Listen timeout - secs
+#define VEND_PXE_MTFTP_DELAY_IX 5 // Transmission timeout - secs
+#define VEND_PXE_DISCOVERY_CONTROL_IX 6 // bit field
+#define VEND_PXE_DISCOVERY_MCAST_ADDR_IX 7 // boot server discovery multicast address
+#define VEND_PXE_BOOT_SERVERS_IX 8 // list of boot servers of form tp(2) cnt(1) ips[cnt]
+#define VEND_PXE_BOOT_MENU_IX 9
+#define VEND_PXE_BOOT_PROMPT_IX 10
+#define VEND_PXE_MCAST_ADDRS_ALLOC_IX 0 // not used by PXE client
+#define VEND_PXE_CREDENTIAL_TYPES_IX 11
+#define VEND_13_IX 0 // not used by PXE client
+#define VEND_14_IX 0 // not used by PXE client
+#define VEND_15_IX 0 // not used by PXE client
+#define VEND_16_IX 0 // not used by PXE client
+#define VEND_17_IX 0 // not used by PXE client
+#define VEND_18_IX 0 // not used by PXE client
+#define VEND_19_IX 0 // not used by PXE client
+#define VEND_20_IX 0 // not used by PXE client
+#define VEND_21_IX 0 // not used by PXE client
+#define VEND_22_IX 0 // not used by PXE client
+#define VEND_23_IX 0 // not used by PXE client
+#define VEND_24_IX 0 // not used by PXE client
+#define VEND_25_IX 0 // not used by PXE client
+#define VEND_26_IX 0 // not used by PXE client
+#define VEND_27_IX 0 // not used by PXE client
+#define VEND_28_IX 0 // not used by PXE client
+#define VEND_29_IX 0 // not used by PXE client
+#define VEND_30_IX 0 // not used by PXE client
+#define VEND_31_IX 0 // not used by PXE client
+#define VEND_32_IX 0 // not used by PXE client
+#define VEND_33_IX 0 // not used by PXE client
+#define VEND_34_IX 0 // not used by PXE client
+#define VEND_35_IX 0 // not used by PXE client
+#define VEND_36_IX 0 // not used by PXE client
+#define VEND_37_IX 0 // not used by PXE client
+#define VEND_38_IX 0 // not used by PXE client
+#define VEND_39_IX 0 // not used by PXE client
+#define VEND_40_IX 0 // not used by PXE client
+#define VEND_41_IX 0 // not used by PXE client
+#define VEND_42_IX 0 // not used by PXE client
+#define VEND_43_IX 0 // not used by PXE client
+#define VEND_44_IX 0 // not used by PXE client
+#define VEND_45_IX 0 // not used by PXE client
+#define VEND_46_IX 0 // not used by PXE client
+#define VEND_47_IX 0 // not used by PXE client
+#define VEND_48_IX 0 // not used by PXE client
+#define VEND_49_IX 0 // not used by PXE client
+#define VEND_50_IX 0 // not used by PXE client
+#define VEND_51_IX 0 // not used by PXE client
+#define VEND_52_IX 0 // not used by PXE client
+#define VEND_53_IX 0 // not used by PXE client
+#define VEND_54_IX 0 // not used by PXE client
+#define VEND_55_IX 0 // not used by PXE client
+#define VEND_56_IX 0 // not used by PXE client
+#define VEND_57_IX 0 // not used by PXE client
+#define VEND_58_IX 0 // not used by PXE client
+#define VEND_59_IX 0 // not used by PXE client
+#define VEND_60_IX 0 // not used by PXE client
+#define VEND_61_IX 0 // not used by PXE client
+#define VEND_62_IX 0 // not used by PXE client
+#define VEND_63_IX 0 // not used by PXE client
+#define VEND_64_IX 0 // not used by PXE client
+#define VEND_65_IX 0 // not used by PXE client
+#define VEND_66_IX 0 // not used by PXE client
+#define VEND_67_IX 0 // not used by PXE client
+#define VEND_68_IX 0 // not used by PXE client
+#define VEND_69_IX 0 // not used by PXE client
+#define VEND_70_IX 0 // not used by PXE client
+#define VEND_PXE_BOOT_ITEM_IX 12
+
+#define MAX_OUR_PXE_OPT VEND_PXE_BOOT_ITEM // largest PXE option in which we are interested
+#define MAX_OUR_PXE_IX VEND_PXE_BOOT_ITEM_IX // largest PXE option index
+//
+// define various types by options that are sent
+//
+#define WfM11a_OPTS ((1<<VEND_PXE_MTFTP_IP_IX) | \
+ (1<<VEND_PXE_MTFTP_CPORT_IX) | \
+ (1<<VEND_PXE_MTFTP_SPORT_IX) | \
+ (1<<VEND_PXE_MTFTP_TMOUT_IX) | \
+ (1<<VEND_PXE_MTFTP_DELAY_IX))
+
+#define DISCOVER_OPTS ((1<<VEND_PXE_DISCOVERY_CONTROL_IX) | \
+ (1<<VEND_PXE_DISCOVERY_MCAST_ADDR_IX) | \
+ (1<<VEND_PXE_BOOT_SERVERS_IX) | \
+ (1<<VEND_PXE_BOOT_MENU_IX) | \
+ (1<<VEND_PXE_BOOT_PROMPT_IX) | \
+ (1<<VEND_PXE_BOOT_ITEM_IX))
+
+#define CREDENTIALS_OPT (1 << VEND_PXE_CREDENTIAL_TYPES_IX)
+
+//
+// definitions of indices to populate option interest array
+//
+#define OP_SUBNET_MASK_IX 1
+#define OP_TIME_OFFSET_IX 0 // not used by PXE client
+#define OP_ROUTER_LIST_IX 2
+#define OP_TIME_SERVERS_IX 0 // not used by PXE client
+#define OP_NAME_SERVERS_IX 0 // not used by PXE client
+#define OP_DNS_SERVERS_IX 0 // not used by PXE client
+#define OP_LOG_SERVERS_IX 0 // not used by PXE client
+#define OP_COOKIE_SERVERS_IX 0 // not used by PXE client
+#define OP_LPR_SREVERS_IX 0 // not used by PXE client
+#define OP_IMPRESS_SERVERS_IX 0 // not used by PXE client
+#define OP_RES_LOC_SERVERS_IX 0 // not used by PXE client
+#define OP_HOST_NAME_IX 0 // not used by PXE client
+#define OP_BOOT_FILE_SZ_IX 9
+#define OP_DUMP_FILE_IX 0 // not used by PXE client
+#define OP_DOMAIN_NAME_IX 0 // not used by PXE client
+#define OP_SWAP_SERVER_IX 0 // not used by PXE client
+#define OP_ROOT_PATH_IX 0 // not used by PXE client
+#define OP_EXTENSION_PATH_IX 0 // not used by PXE client
+#define OP_IP_FORWARDING_IX 0 // not used by PXE client
+#define OP_NON_LOCAL_SRC_RTE_IX 0 // not used by PXE client
+#define OP_POLICY_FILTER_IX 0 // not used by PXE client
+#define OP_MAX_DATAGRAM_SZ_IX 0 // not used by PXE client
+#define OP_DEFAULT_TTL_IX 0 // not used by PXE client
+#define OP_MTU_AGING_TIMEOUT_IX 0 // not used by PXE client
+#define OP_MTU_SIZES_IX 0 // not used by PXE client
+#define OP_MTU_TO_USE_IX 0 // not used by PXE client
+#define OP_ALL_SUBNETS_LOCAL_IX 0 // not used by PXE client
+#define OP_BROADCAST_ADD_IX 0 // not used by PXE client
+#define OP_PERFORM_MASK_DISCOVERY_IX 0 // not used by PXE client
+#define OP_RESPOND_TO_MASK_REQ_IX 0 // not used by PXE client
+#define OP_PERFORM_ROUTER_DISCOVERY_IX 0 // not used by PXE client
+#define OP_ROUTER_SOLICIT_ADDRESS_IX 0 // not used by PXE client
+#define OP_STATIC_ROUTER_LIST_IX 0 // not used by PXE client
+#define OP_USE_ARP_TRAILERS_IX 0 // not used by PXE client
+#define OP_ARP_CACHE_TIMEOUT_IX 0 // not used by PXE client
+#define OP_ETHERNET_ENCAPSULATION_IX 0 // not used by PXE client
+#define OP_TCP_DEFAULT_TTL_IX 0 // not used by PXE client
+#define OP_TCP_KEEP_ALIVE_INT_IX 0 // not used by PXE client
+#define OP_KEEP_ALIVE_GARBAGE_IX 0 // not used by PXE client
+#define OP_NIS_DOMAIN_NAME_IX 0 // not used by PXE client
+#define OP_NIS_SERVERS_IX 0 // not used by PXE client
+#define OP_NTP_SERVERS_IX 0 // not used by PXE client
+#define OP_VENDOR_SPECIFIC_IX 3
+#define OP_NBNS_SERVERS_IX 0 // not used by PXE client
+#define OP_NBDD_SERVERS_IX 0 // not used by PXE client
+#define OP_NETBIOS_NODE_TYPE_IX 0 // not used by PXE client
+#define OP_NETBIOS_SCOPE_IX 0 // not used by PXE client
+#define OP_XWINDOW_SYSTEM_FONT_SERVERS_IX 0 // not used by PXE client
+#define OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS_IX 0 // not used by PXE client
+// DHCP option indices
+//
+#define OP_DHCP_REQ_IP_ADD_IX 0 // not used by PXE client
+#define OP_DHCP_LEASE_TIME_IX 0 // not used by PXE client
+#define OP_DHCP_OPTION_OVERLOAD_IX 4
+#define OP_DHCP_MESSAGE_TYPE_IX 5
+#define OP_DHCP_SERVER_IP_IX 6
+#define OP_DHCP_PARM_REQ_LIST_IX 0 // not used by PXE client
+#define OP_DHCP_ERROR_MESSAGE_IX 0 // not used by PXE client
+#define OP_DHCP_MAX_MESSAGE_SZ_IX 0 // not used by PXE client
+#define OP_DHCP_RENEWAL_TIME_IX 0 // not used by PXE client
+#define OP_DHCP_REBINDING_TIME_IX 0 // not used by PXE client
+#define OP_DHCP_CLASS_IDENTIFIER_IX 7
+#define OP_DHCP_CLIENT_IDENTIFIER_IX 0 // not used by PXE client
+#define OP_RESERVED62_IX 0 // not used by PXE client
+#define OP_RESERVED63_IX 0 // not used by PXE client
+#define OP_NISPLUS_DOMAIN_NAME_IX 0 // not used by PXE client
+#define OP_NISPLUS_SERVERS_IX 0 // not used by PXE client
+#define OP_DHCP_TFTP_SERVER_NAME_IX 0 // not used by PXE client
+#define OP_DHCP_BOOTFILE_IX 8
+
+#define MAX_OUR_OPT OP_DHCP_BOOTFILE // largest option in which we are interested
+#define MAX_OUR_IX OP_BOOT_FILE_SZ_IX
+
+typedef struct {
+ DHCPV4_OP_STRUCT *PktOptAdds[MAX_OUR_IX];
+ DHCPV4_OP_STRUCT *PxeOptAdds[MAX_OUR_PXE_IX];
+ UINT8 Status;
+} OPTION_POINTERS;
+
+typedef struct DhcpReceiveBufferStruct {
+ union {
+ UINT8 ReceiveBuffer[MAX_DHCP_MSG_SZ];
+ DHCPV4_STRUCT Dhcpv4;
+ } u;
+
+ OPTION_POINTERS OpAdds;
+} DHCP_RECEIVE_BUFFER;
+
+#define PXE_TYPE (1 << 0)
+#define WfM11a_TYPE (1 << 1)
+#define DISCOVER_TYPE (1 << 2)
+#define CREDENTIALS_TYPE (1 << 3)
+#define USE_THREE_BYTE (1 << 4)
+
+#endif // _DHCP_H
+
+/* EOF - dhcp.h */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.c
new file mode 100644
index 0000000000..026dd249e0
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.c
@@ -0,0 +1,46 @@
+/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeArch.c
+
+Abstract:
+ Defines PXE Arch type
+
+
+**/
+
+
+#include "PxeArch.h"
+
+UINT16 mSysArch = 0;
+
+UINT16
+GetSysArch (
+ VOID
+ )
+{
+ if (mSysArch == 0) {
+ //
+ // This is first call
+ // Assign to invalid value
+ //
+ mSysArch = 0xFFFF;
+
+ //
+ // We do not know what is EBC architecture.
+ // Maybe we can try to locate DebugSupport protocol to get ISA.
+ // TBD now.
+ //
+ }
+
+ return mSysArch;
+}
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h
new file mode 100644
index 0000000000..0db856b163
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h
@@ -0,0 +1,36 @@
+/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeArch.h
+
+Abstract:
+ Defines PXE Arch type
+
+
+**/
+
+#ifndef _EFI_PXE_ARCH_H_
+#define _EFI_PXE_ARCH_H_
+
+//
+// warning #175: subscript out of range
+//
+#pragma warning (disable: 175)
+
+#define SYS_ARCH GetSysArch()
+
+UINT16
+GetSysArch (
+ VOID
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Hton.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Hton.h
new file mode 100644
index 0000000000..c4ace49e78
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Hton.h
@@ -0,0 +1,43 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ hton.h
+
+Abstract:
+ Byte swapping macros.
+
+
+**/
+
+#ifndef _HTON_H_
+#define _HTON_H_
+
+//
+// Only Intel order functions are defined at this time.
+//
+#define HTONS(v) (UINT16) ((((v) << 8) & 0xff00) + (((v) >> 8) & 0x00ff))
+
+#define HTONL(v) \
+ (UINT32) ((((v) << 24) & 0xff000000) + (((v) << 8) & 0x00ff0000) + (((v) >> 8) & 0x0000ff00) + (((v) >> 24) & 0x000000ff))
+
+#define HTONLL(v) swap64 (v)
+
+#define U8PTR(na) ((UINT8 *) &(na))
+
+#define NTOHS(ns) ((UINT16) (((*U8PTR (ns)) << 8) +*(U8PTR (ns) + 1)))
+
+#define NTOHL(ns) \
+ ((UINT32) (((*U8PTR (ns)) << 24) + ((*(U8PTR (ns) + 1)) << 16) + ((*(U8PTR (ns) + 2)) << 8) +*(U8PTR (ns) + 3)))
+
+#endif /* _HTON_H_ */
+
+/* EOF - hton.h */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h
new file mode 100644
index 0000000000..cc878d8dd7
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h
@@ -0,0 +1,26 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeArch.h
+
+Abstract:
+ Defines PXE Arch type
+
+
+**/
+
+#ifndef _EFI_PXE_ARCH_H_
+#define _EFI_PXE_ARCH_H_
+
+#define SYS_ARCH 0x6
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h
new file mode 100644
index 0000000000..a482eb9cd9
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h
@@ -0,0 +1,736 @@
+/** @file
+
+Copyright (c) 2004 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+**/
+
+#ifndef _IP_H_
+#define _IP_H_
+
+#include "hton.h"
+
+//
+// portability macros
+//
+#define UDP_FILTER_MASK (EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | \
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT | \
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | \
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT | \
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER \
+ )
+
+#define PXE_BOOT_LAYER_MASK 0x7FFF
+#define PXE_BOOT_LAYER_INITIAL 0x0000
+#define PXE_BOOT_LAYER_CREDENTIAL_FLAG 0x8000
+#define MAX_BOOT_SERVERS 32
+
+//
+// macro to evaluate IP address as TRUE if it is a multicast IP address
+//
+#define IS_MULTICAST(ptr) ((*((UINT8 *) ptr) & 0xf0) == 0xe0)
+
+//
+// length macros
+//
+#define IP_ADDRESS_LENGTH(qp) (((qp)->UsingIpv6) ? sizeof (EFI_IPv6_ADDRESS) : sizeof (EFI_IPv4_ADDRESS))
+
+#define MAX_FRAME_DATA_SIZE 1488
+#define ALLOCATE_SIZE(X) (((X) + 7) & 0xfff8)
+#define MODE_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_MODE))
+#define BUFFER_ALLOCATE_SIZE (8192 + 512)
+#define ROUTER_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_PXE_BASE_CODE_ROUTE_ENTRY) * PXE_ROUTER_TABLE_SIZE))
+#define ARP_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_PXE_BASE_CODE_ARP_ENTRY) * PXE_ARP_CACHE_SIZE))
+#define FILTER_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_IP_ADDRESS) * PXE_IP_FILTER_SIZE))
+#define PXE_ARP_CACHE_SIZE 8
+#define PXE_ROUTER_TABLE_SIZE 8
+#define PXE_IP_FILTER_SIZE 8
+#define ICMP_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR))
+#define TFTP_ERR_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_TFTP_ERROR))
+
+//
+// DHCP discover/request packets are sent to this UDP port. ProxyDHCP
+// servers listen on this port for DHCP discover packets that have a
+// class identifier (option 60) with 'PXEClient' in the first 9 bytes.
+// Bootservers also listen on this port for PXE broadcast discover
+// requests from PXE clients.
+//
+#define DHCP_SERVER_PORT 67
+
+//
+// When DHCP, proxyDHCP and Bootservers respond to DHCP and PXE broadcast
+// discover requests by broadcasting the reply packet, the packet is
+// broadcast to this port.
+//
+#define DHCP_CLIENT_PORT 68
+
+//
+// TFTP servers listen for TFTP open requests on this port.
+//
+#define TFTP_OPEN_PORT 69
+
+//
+// proxyDHCP and Bootservers listen on this port for a PXE unicast and/or
+// multicast discover requests from PXE clients. A PXE discover request
+// looks like a DHCP discover or DHCP request packet.
+//
+#define PXE_DISCOVERY_PORT 4011
+
+//
+// This port is used by the PXE client/server protocol tests.
+//
+#define PXE_PORT_PXETEST_PORT 0x8080
+
+//
+// Definitions for Ethertype protocol numbers and interface types
+// Per RFC 1700,
+//
+#define PXE_PROTOCOL_ETHERNET_IP 0x0800
+#define PXE_PROTOCOL_ETHERNET_ARP 0x0806
+#define PXE_PROTOCOL_ETHERNET_RARP 0x8035
+
+#define PXE_IFTYPE_ETHERNET 0x01
+#define PXE_IFTYPE_TOKENRING 0x04
+#define PXE_IFTYPE_FIBRE_CHANNEL 0x12
+
+//
+// Definitions for internet protocol version 4 header
+// Per RFC 791, September 1981.
+//
+#define IPVER4 4
+
+#pragma pack(1) // make network structures packed byte alignment
+typedef union {
+ UINT8 B[4];
+ UINT32 L;
+} IPV4_ADDR;
+
+#define IPV4_HEADER_LENGTH(IpHeaderPtr) (((IpHeaderPtr)->VersionIhl & 0xf) << 2)
+
+#define SET_IPV4_VER_HDL(IpHeaderPtr, IpHeaderLen) { \
+ (IpHeaderPtr)->VersionIhl = (UINT8) ((IPVER4 << 4) | ((IpHeaderLen) >> 2)); \
+ }
+
+typedef struct {
+ UINT8 VersionIhl;
+ UINT8 TypeOfService;
+ UINT16 TotalLength;
+ UINT16 Id;
+ UINT16 FragmentFields;
+ UINT8 TimeToLive;
+ UINT8 Protocol;
+ UINT16 HeaderChecksum;
+ IPV4_ADDR SrcAddr;
+ IPV4_ADDR DestAddr;
+ //
+ // options are not implemented
+ //
+} IPV4_HEADER;
+
+#define IP_FRAG_RSVD 0x8000 // reserved bit - must be zero
+#define IP_NO_FRAG 0x4000 // do not fragment bit
+#define IP_MORE_FRAG 0x2000 // not last fragment
+#define IP_FRAG_OFF_MSK 0x1fff // fragment offset in 8 byte chunks
+#define DEFAULT_RFC_TTL 64
+
+#define PROT_ICMP 1
+#define PROT_IGMP 2
+#define PROT_TCP 6
+#define PROT_UDP 17
+
+/*
+ * Definitions for internet control message protocol version 4 message
+ * structure. Per RFC 792, September 1981.
+ */
+
+//
+// icmp header for all icmp messages
+//
+typedef struct {
+ UINT8 Type; // message type
+ UINT8 Code; // type specific - 0 for types we implement
+ UINT16 Checksum; // ones complement of ones complement sum of 16 bit words of message
+} ICMPV4_HEADER;
+
+#define ICMP_DEST_UNREACHABLE 3
+#define ICMP_SOURCE_QUENCH 4
+#define ICMP_REDIRECT 5
+#define ICMP_ECHO 8
+#define ICMP_ECHO_REPLY 0
+#define ICMP_ROUTER_ADV 9
+#define ICMP_ROUTER_SOLICIT 10
+#define ICMP_TIME_EXCEEDED 11
+#define ICMP_PARAMETER_PROBLEM 12
+#define ICMP_TIMESTAMP 13
+#define ICMP_TIMESTAMP_REPLY 14
+#define ICMP_INFO_REQ 15
+#define ICMP_INFO_REQ_REPLY 16
+#define ICMP_SUBNET_MASK_REQ 17
+#define ICMP_SUBNET_MASK_REPLY 18
+//
+// other ICMP message types ignored in this implementation
+//
+// icmp general messages
+//
+typedef struct {
+ ICMPV4_HEADER Header;
+ //
+ // generally unused except byte [0] for
+ // parameter problem message
+ //
+ UINT8 GenerallyUnused[4];
+ //
+ // original message ip header of plus 64
+ // bits of data
+ //
+ IPV4_HEADER IpHeader;
+} ICMPV4_GENERAL_MESSAGE;
+
+//
+// icmp req/rply message header
+//
+typedef struct {
+ ICMPV4_HEADER Header;
+ UINT16 Id;
+ UINT16 SequenceNumber;
+} ICMPV4_REQUEST_REPLY_HEADER;
+
+//
+// icmp echo message
+//
+typedef struct {
+ ICMPV4_REQUEST_REPLY_HEADER Header;
+ UINT8 EchoData[1]; // variable length data to be echoed
+} ICMPV4_ECHO_MESSAGE;
+
+//
+// icmp timestamp message - times are milliseconds since midnight UT -
+// if non std, set high order bit
+//
+typedef struct {
+ ICMPV4_REQUEST_REPLY_HEADER Header;
+ UINT32 OriginalTime; // originating timestamp
+ UINT32 ReceiveTime; // receiving timestamp
+ UINT32 TransmitTime; // transmitting timestamp
+} ICMPV4_TIMESTAMP_MESSAGE;
+
+//
+// icmp info request structure - fill in source and dest net ip address on reply
+//
+typedef struct {
+ ICMPV4_REQUEST_REPLY_HEADER Header;
+} ICMPV4_INFO_MESSAGE;
+
+//
+// Definitions for internet control message protocol version 4 message structure
+// Router discovery
+// Per RFC 1256, September 1991.
+//
+//
+// icmp router advertisement message
+//
+typedef struct {
+ ICMPV4_HEADER Header;
+ UINT8 NumberEntries; // number of address entries
+ UINT8 EntrySize; // number of 32 bit words per address entry
+ UINT16 Lifetime; // seconds to consider info valid
+ UINT32 RouterIp;
+ UINT32 Preferance;
+} ICMPV4_ROUTER_ADVERTISE_MESSAGE;
+
+//
+// icmp router solicitation message
+//
+typedef struct {
+ ICMPV4_HEADER Header;
+ UINT32 Reserved;
+} ICMPV4_ROUTER_SOLICIT_MESSAGE;
+
+#define MAX_SOLICITATION_DELAY 1 // 1 second
+#define SOLICITATION_INTERVAL 3 // 3 seconds
+#define MAX_SOLICITATIONS 3 // 3 transmissions
+#define V1ROUTER_PRESENT_TIMEOUT 400 // 400 second timeout until v2 reports can be sent
+#define UNSOLICITED_REPORT_INTERVAL 10 // 10 seconds between unsolicited reports
+#define BROADCAST_IPv4 0xffffffff
+
+//
+// Definitions for address resolution protocol message structure
+// Per RFC 826, November 1982
+//
+typedef struct {
+ UINT16 HwType; // hardware type - e.g. ethernet (1)
+ UINT16 ProtType; // protocol type - for ethernet, 0x800 for IP
+ UINT8 HwAddLen; // byte length of a hardware address (e.g. 6 for ethernet)
+ UINT8 ProtAddLen; // byte length of a protocol address (e.g. 4 for ipv4)
+ UINT16 OpCode;
+ //
+ // source and dest hw and prot addresses follow - see example below
+ //
+} ARP_HEADER;
+
+#define ETHERNET_ADD_SPC 1
+
+#define ETHER_TYPE_IP 0x800
+
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+
+//
+// generic ARP packet
+//
+typedef struct {
+ ARP_HEADER ArpHeader;
+ EFI_MAC_ADDRESS SrcHardwareAddr;
+ EFI_IP_ADDRESS SrcProtocolAddr;
+ EFI_MAC_ADDRESS DestHardwareAddr;
+ EFI_IP_ADDRESS DestProtocolAddr;
+} ARP_PACKET;
+
+#define ENET_HWADDLEN 6
+#define IPV4_PROTADDLEN 4
+
+//
+// Definitions for user datagram protocol version 4 pseudo header & header
+// Per RFC 768, 28 August 1980
+//
+typedef struct {
+ IPV4_ADDR SrcAddr; // source ip address
+ IPV4_ADDR DestAddr; // dest ip address
+ UINT8 Zero; // 0
+ UINT8 Protocol; // protocol
+ UINT16 TotalLength; // UDP length - sizeof udpv4hdr + data length
+} UDPV4_PSEUDO_HEADER;
+
+typedef struct {
+ UINT16 SrcPort; // source port identifier
+ UINT16 DestPort; // destination port identifier
+ UINT16 TotalLength; // total length header plus data
+ //
+ // ones complement of ones complement sum of 16 bit
+ // words of pseudo header, UDP header, and data
+ // zero checksum is transmitted as -0 (ones comp)
+ // zero transmitted means checksum not computed
+ // data follows
+ //
+ UINT16 Checksum;
+} UDPV4_HEADER;
+
+typedef struct {
+ UDPV4_PSEUDO_HEADER Udpv4PseudoHeader;
+ UDPV4_HEADER Udpv4Header;
+} UDPV4_HEADERS;
+
+//
+// Definitions for transmission control protocol header
+// Per RFC 793, September, 1981
+//
+typedef struct {
+ IPV4_ADDR SrcAddr; // source ip address
+ IPV4_ADDR DestAddr; // dest ip address
+ UINT8 Zero; // 0
+ UINT8 Protocol; // protocol
+ UINT16 TotalLength; // TCP length - TCP header length + data length
+} TCPV4_PSEUDO_HEADER;
+
+typedef struct {
+ UINT16 SrcPort; // source port identifier
+ UINT16 DestPort; // destination port identifier
+ UINT32 SeqNumber; // Sequence number
+ UINT32 AckNumber; // Acknowledgement Number
+ //
+ // Nibble of HLEN (length of header in 32-bit multiples)
+ // 6bits of RESERVED
+ // Nibble of Code Bits
+ //
+ UINT16 HlenResCode;
+ UINT16 Window; // Software buffer size (sliding window size) in network-standard byte order
+ //
+ // ones complement of ones complement sum of 16 bit words of
+ // pseudo header, TCP header, and data
+ // zero checksum is transmitted as -0 (ones comp)
+ // zero transmitted means checksum not computed
+ //
+ UINT16 Checksum;
+ UINT16 UrgentPointer; // pointer to urgent data (allows sender to specify urgent data)
+} TCPV4_HEADER;
+
+typedef struct {
+ TCPV4_PSEUDO_HEADER Tcpv4PseudoHeader;
+ TCPV4_HEADER Tcpv4Header;
+} TCPV4_HEADERS;
+
+typedef struct {
+ UINT8 Kind; // one of the following:
+ UINT8 Length; // total option length including Kind and Lth
+ UINT8 Data[1]; // length = Lth - 2
+} TCPV4_OPTION;
+
+#define TCP_OP_END 0 // only used to pad to end of TCP header
+#define TCP_NOP 1 // optional - may be used to pad between options to get alignment
+#define TCP_MAX_SEG 2 // maximum receive segment size - only send at initial connection request
+#define MAX_MEDIA_HDR_SIZE 64
+#define MIN_ENET_DATA_SIZE 64
+#define MAX_ENET_DATA_SIZE 1500 // temp def - make a network based var
+#define MAX_IPV4_PKT_SIZE 65535 // maximum IP packet size
+#define MAX_IPV4_DATA_SIZE (MAX_IPV4_PKT_SIZE - sizeof (IPV4_HEADER))
+#define MAX_IPV4_FRAME_DATA_SIZE (MAX_FRAME_DATA_SIZE - sizeof (IPV4_HEADER))
+#define REAS_IPV4_PKT_SIZE 576 // minimum IP packet size all IP host can handle
+#define REAS_IPV4_DATA_SIZE (REAS_IPV4_PKT_SIZE - sizeof (IPV4_HEADER))
+
+//
+//
+//
+typedef union {
+ UINT8 Data[MAX_ENET_DATA_SIZE];
+ ICMPV4_HEADER IcmpHeader;
+ IGMPV2_MESSAGE IgmpMessage;
+ struct {
+ UDPV4_HEADER UdpHeader;
+ UINT8 Data[1];
+ } Udp;
+ struct {
+ TCPV4_HEADER TcpHeader;
+ UINT8 Data[1];
+ } Tcp;
+} PROTOCOL_UNION;
+
+//
+// out buffer structure
+//
+typedef struct {
+ UINT8 MediaHeader[MAX_MEDIA_HDR_SIZE];
+ IPV4_HEADER IpHeader;
+ //
+ // following union placement only valid if no option IP header
+ //
+ PROTOCOL_UNION u;
+} IPV4_BUFFER;
+
+typedef struct {
+ IPV4_HEADER IpHeader;
+ //
+ // following union placement only valid if no option IP header
+ //
+ PROTOCOL_UNION u;
+} IPV4_STRUCT;
+
+#pragma pack() // reset to default
+
+ ////////////////////////////////////////////////////////////
+//
+// BC IP Filter Routine
+//
+EFI_STATUS
+IpFilter (
+ PXE_BASECODE_DEVICE *Private,
+ IN EFI_PXE_BASE_CODE_IP_FILTER *Filter
+ )
+;
+
+//
+// //////////////////////////////////////////////////////////////////////
+//
+// Udp Write Routine - called by base code - e.g. TFTP - already locked
+//
+EFI_STATUS
+UdpWrite (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN UINT16 OpFlags,
+ IN EFI_IP_ADDRESS *DestIpPtr,
+ IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortptr,
+ IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL
+ IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL
+ IN UINTN *HeaderSizePtr, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN UINTN *BufferSizePtr,
+ IN VOID *BufferPtr
+ )
+;
+
+//
+// /////////////////////////////////////////////////////////////////////
+//
+// Udp Read Routine - called by base code - e.g. TFTP - already locked
+//
+EFI_STATUS
+UdpRead (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIpPtr, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPorPtrt, OPTIONAL
+ IN OUT EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL
+ IN UINTN *HeaderSizePtr, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN OUT UINTN *BufferSizePtr,
+ IN VOID *BufferPtr,
+ IN EFI_EVENT TimeoutEvent
+ )
+;
+
+VOID
+IgmpLeaveGroup (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *
+ )
+;
+
+VOID
+IgmpJoinGroup (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *
+ )
+;
+
+//
+// convert number to zero filled ascii value of length lth
+//
+VOID
+CvtNum (
+ UINTN Number,
+ UINT8 *BufferPtr,
+ INTN BufferLen
+ )
+;
+
+//
+// convert number to ascii string at ptr
+//
+VOID
+UtoA10 (
+ UINTN Number,
+ UINT8 *BufferPtr
+ )
+;
+
+//
+// convert ascii numeric string to UINTN
+//
+UINTN
+AtoU (
+ UINT8 *BufferPtr
+ )
+;
+
+UINT64
+AtoU64 (
+ UINT8 *BufferPtr
+ )
+;
+
+//
+// calculate the internet checksum (RFC 1071)
+// return 16 bit ones complement of ones complement sum of 16 bit words
+//
+UINT16
+IpChecksum (
+ UINT16 *MessagePtr,
+ UINTN ByteLength
+ )
+;
+
+//
+// do checksum on non contiguous header and data
+//
+UINT16
+IpChecksum2 (
+ UINT16 *Header,
+ UINTN HeaderLength,
+ UINT16 *Message,
+ UINTN MessageLength
+ )
+;
+
+//
+// update checksum when only a single word changes
+//
+UINT16
+UpdateChecksum (
+ UINT16 OldChecksum,
+ UINT16 OldWord,
+ UINT16 NewWord
+ )
+;
+
+VOID
+SeedRandom (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN UINT16 InitialSeed
+ )
+;
+
+UINT16
+Random (
+ IN PXE_BASECODE_DEVICE *Private
+ )
+;
+
+EFI_STATUS
+SendPacket (
+ PXE_BASECODE_DEVICE *Private,
+ VOID *HeaderPtr,
+ VOID *PacketPtr,
+ INTN PacketLength,
+ VOID *HardwareAddress,
+ UINT16 MediaProtocol,
+ IN EFI_PXE_BASE_CODE_FUNCTION Function
+ )
+;
+
+VOID
+HandleArpReceive (
+ PXE_BASECODE_DEVICE *Private,
+ ARP_PACKET *ArpPacketPtr,
+ VOID *HeaderPtr
+ )
+;
+
+VOID
+HandleIgmp (
+ PXE_BASECODE_DEVICE *Private,
+ IGMPV2_MESSAGE *IgmpMessageptr,
+ UINTN IgmpMessageLen
+ )
+;
+
+VOID
+IgmpCheckTimers (
+ PXE_BASECODE_DEVICE *Private
+ )
+; // poll when doing a receive
+// return hw add of IP and TRUE if available, otherwise FALSE
+//
+BOOLEAN
+GetHwAddr (
+ IN PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *ProtocolAddressPtr,
+ EFI_MAC_ADDRESS *HardwareAddressPtr
+ )
+;
+
+EFI_STATUS
+DoArp (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN EFI_IP_ADDRESS *ProtocolAddressPtr,
+ OUT EFI_MAC_ADDRESS *HardwareAddressptr
+ )
+;
+
+BOOLEAN
+OnSameSubnet (
+ UINTN IpAddressLen,
+ EFI_IP_ADDRESS *Ip1,
+ EFI_IP_ADDRESS *Ip2,
+ EFI_IP_ADDRESS *SubnetMask
+ )
+;
+
+VOID
+IpAddRouter (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *RouterIp
+ )
+;
+
+#define Ip4AddRouter(Private, Ipv4Ptr) IpAddRouter (Private, (EFI_IP_ADDRESS *) Ipv4Ptr)
+
+//
+// routine to send ipv4 packet
+// ipv4 + upper protocol header for length TotHdrLth in xmtbuf, ipv4 header length IpHdrLth
+// routine fills in ipv4hdr Ver_Hdl, TotLth, and Checksum, moves in Data, and gets dest MAC address
+//
+EFI_STATUS
+Ipv4Xmt (
+ PXE_BASECODE_DEVICE *Private,
+ UINT32 GatewayIP,
+ UINTN IpHeaderLen,
+ UINTN TotalHeaderLen,
+ VOID *Data,
+ UINTN DataLen,
+ EFI_PXE_BASE_CODE_FUNCTION Function
+ )
+;
+
+//
+// send ipv4 packet with ipv4 option
+//
+EFI_STATUS
+Ipv4SendWOp (
+ PXE_BASECODE_DEVICE *Private,
+ UINT32 GatewayIP,
+ UINT8 *MessagePtr,
+ UINTN MessageLth,
+ UINT8 Protocol,
+ UINT8 *Option,
+ UINTN OptionLen,
+ UINT32 DestIp,
+ EFI_PXE_BASE_CODE_FUNCTION Function
+ )
+;
+
+//
+// send MsgLth message at MsgPtr - higher level protocol header already in xmtbuf, length HdrSize
+//
+EFI_STATUS
+Ip4Send (
+ IN PXE_BASECODE_DEVICE *Private, // pointer to instance data
+ IN UINTN MayFragment, //
+ IN UINT8 Protocol, // protocol
+ IN UINT32 SrcIp, // Source IP address
+ IN UINT32 DestIp, // Destination IP address
+ IN UINT32 GatewayIp, // used if not NULL and needed
+ IN UINTN HeaderSize, // protocol header byte length
+ IN UINT8 *MsgPtr, // pointer to data
+ IN UINTN MsgLength
+ )
+; // data byte length
+// receive up to MsgLth message into MsgPtr for protocol Prot
+// return message length, src/dest ips if select any, and pointer to protocol header
+//
+EFI_STATUS
+IpReceive (
+ IN PXE_BASECODE_DEVICE *Private, // pointer to instance data
+ UINT16 OpFlags, // Flags to determine if filtering on IP addresses
+ EFI_IP_ADDRESS *SrcIpPtr, // if filtering, O if accept any
+ EFI_IP_ADDRESS *DstIpPtr, // if filtering, O if accept any
+ UINT8 Protocol, // protocol
+ VOID *HeaderPtr, // address of where to put protocol header
+ UINTN HeaderSize, // protocol header byte length
+ UINT8 *MsgPtr, // pointer to data buffer
+ UINTN *MsgLenPtr, // pointer to data buffer length/ O - returned data length
+ IN EFI_EVENT TimeoutEvent
+ )
+;
+
+#if 0
+VOID
+WaitForTxComplete (
+ IN PXE_BASECODE_DEVICE *Private
+ )
+;
+#endif
+//
+// routine to cycle waiting for a receive or timeout
+//
+EFI_STATUS
+WaitForReceive (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN EFI_PXE_BASE_CODE_FUNCTION Function,
+ IN EFI_EVENT TimeoutEvent,
+ IN OUT UINTN *HeaderSizePtr,
+ IN OUT UINTN *BufferSizePtr,
+ IN OUT UINT16 *ProtocolPtr
+ )
+;
+
+#endif /* _IP_H_ */
+
+/* EOF - ip.h */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h
new file mode 100644
index 0000000000..6d06045df0
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h
@@ -0,0 +1,26 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeArch.h
+
+Abstract:
+ Defines PXE Arch type
+
+
+**/
+
+#ifndef _EFI_PXE_ARCH_H_
+#define _EFI_PXE_ARCH_H_
+
+#define SYS_ARCH 0x2
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf
new file mode 100644
index 0000000000..f5eaa0f622
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf
@@ -0,0 +1,92 @@
+#/** @file
+# Component name for module BC
+#
+# Copyright (c) 2007, Intel Corporation
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PxeBcDxe
+ FILE_GUID = A3f436EA-A127-4EF8-957C-8048606FF670
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = InitializeBCDriver
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ Pxe_bc_mtftp.c
+ Bc.c
+ Dhcp.h
+ Ip.h
+ Pxe_bc_ip.c
+ Pxe_bc_dhcp.c
+ Pxe_bc_arp.c
+ Hton.h
+ ComponentName.c
+ Bc.h
+ Pxe_loadfile.c
+ Tftp.h
+ Pxe_bc_igmp.c
+ Pxe_bc_udp.c
+
+[Sources.IA32]
+ Ia32\PxeArch.h
+
+[Sources.X64]
+ X64\PxeArch.h
+
+[Sources.IPF]
+ Ipf\PxeArch.h
+
+[Sources.EBC]
+ Ebc\PxeArch.h
+ Ebc\PxeArch.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+
+
+[Guids]
+ gEfiSmbiosTableGuid # ALWAYS_CONSUMED
+
+
+[Protocols]
+ gEfiBisProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiPxeBaseCodeCallbackProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiPxeBaseCodeProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiLoadFileProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiNetworkInterfaceIdentifierProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcpProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiNetworkInterfaceIdentifierProtocolGuid_31 # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa
new file mode 100644
index 0000000000..1a1f81d1e3
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa
@@ -0,0 +1,106 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>BC</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>A3f436EA-A127-4EF8-957C-8048606FF670</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module BC</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>BC</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseMemoryLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>pxe_bc_udp.c</Filename>
+ <Filename>pxe_bc_igmp.c</Filename>
+ <Filename>tftp.h</Filename>
+ <Filename>pxe_loadfile.c</Filename>
+ <Filename>bc.h</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>BcEntry.c</Filename>
+ <Filename>ipf\PxeArch.h</Filename>
+ <Filename>ebc\PxeArch.h</Filename>
+ <Filename SupArchList="X64">x64\PxeArch.h</Filename>
+ <Filename>pxe_bc_tcp.c</Filename>
+ <Filename>hton.h</Filename>
+ <Filename>pxe_bc_arp.c</Filename>
+ <Filename>pxe_bc_dhcp.c</Filename>
+ <Filename>pxe_bc_ip.c</Filename>
+ <Filename>ip.h</Filename>
+ <Filename>dhcp.h</Filename>
+ <Filename>bc.c</Filename>
+ <Filename>pxe_bc_mtftp.c</Filename>
+ <Filename>ia32\PxeArch.h</Filename>
+ <Filename>ebc\PxeArch.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiNetworkInterfaceIdentifierProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiDevicePathProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiSimpleNetworkProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiLoadFileProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiPxeBaseCodeProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiPxeBaseCodeCallbackProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiBisProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Guids>
+ <GuidCNames Usage="ALWAYS_CONSUMED">
+ <GuidCName>gEfiSmbiosTableGuid</GuidCName>
+ </GuidCNames>
+ </Guids>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>InitializeBCDriver</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c
new file mode 100644
index 0000000000..3654363e8e
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c
@@ -0,0 +1,583 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ pxe_bc_arp.c
+
+Abstract:
+
+
+**/
+
+#include "Bc.h"
+
+//
+// Definitions for ARP
+// Per RFC 826
+//
+STATIC ARP_HEADER ArpHeader;
+
+#pragma pack(1)
+STATIC struct {
+ UINT8 MediaHeader[14];
+ ARP_HEADER ArpHeader;
+ UINT8 ArpData[64];
+} ArpReplyPacket;
+#pragma pack()
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return none
+
+**/
+VOID
+InitArpHeader (
+ VOID
+ )
+{
+ ArpHeader.HwType = HTONS (ETHERNET_ADD_SPC);
+ ArpHeader.ProtType = HTONS (ETHER_TYPE_IP);
+ ArpHeader.HwAddLen = ENET_HWADDLEN;
+ ArpHeader.ProtAddLen = IPV4_PROTADDLEN;
+ ArpHeader.OpCode = HTONS (ARP_REQUEST);
+
+ CopyMem (&ArpReplyPacket.ArpHeader, &ArpHeader, sizeof (ARP_HEADER));
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+VOID
+HandleArpReceive (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN ARP_PACKET *ArpPacketPtr,
+ IN VOID *MediaHeader
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_MAC_ADDRESS TmpMacAddr;
+ UINTN Index;
+ UINT8 *SrcHwAddr;
+ UINT8 *SrcPrAddr;
+ UINT8 *DstHwAddr;
+ UINT8 *DstPrAddr;
+ UINT8 *TmpPtr;
+
+ //
+ //
+ //
+ PxeBcMode = Private->EfiBc.Mode;
+ SnpMode = Private->SimpleNetwork->Mode;
+
+ //
+ // For now only ethernet addresses are supported.
+ // This will need to be updated when other media
+ // layers are supported by PxeBc, Snp and UNDI.
+ //
+ if (ArpPacketPtr->ArpHeader.HwType != HTONS (ETHERNET_ADD_SPC)) {
+ return ;
+ }
+ //
+ // For now only IP protocol addresses are supported.
+ // This will need to be updated when other protocol
+ // types are supported by PxeBc, Snp and UNDI.
+ //
+ if (ArpPacketPtr->ArpHeader.ProtType != HTONS (ETHER_TYPE_IP)) {
+ return ;
+ }
+ //
+ // For now only SNP hardware address sizes are supported.
+ //
+ if (ArpPacketPtr->ArpHeader.HwAddLen != SnpMode->HwAddressSize) {
+ return ;
+ }
+ //
+ // For now only PxeBc protocol address sizes are supported.
+ //
+ if (ArpPacketPtr->ArpHeader.ProtAddLen != Private->IpLength) {
+ return ;
+ }
+ //
+ // Ignore out of range opcodes
+ //
+ switch (ArpPacketPtr->ArpHeader.OpCode) {
+ case HTONS (ARP_REPLY):
+ case HTONS (ARP_REQUEST):
+ break;
+
+ default:
+ return ;
+ }
+ //
+ // update entry in our ARP cache if we have it
+ //
+ SrcHwAddr = (UINT8 *) &ArpPacketPtr->SrcHardwareAddr;
+ SrcPrAddr = SrcHwAddr + SnpMode->HwAddressSize;
+
+ for (Index = 0; Index < PxeBcMode->ArpCacheEntries; ++Index) {
+ if (CompareMem (
+ &PxeBcMode->ArpCache[Index].IpAddr,
+ SrcPrAddr,
+ Private->IpLength
+ )) {
+ continue;
+ }
+
+ CopyMem (
+ &PxeBcMode->ArpCache[Index].MacAddr,
+ SrcHwAddr,
+ SnpMode->HwAddressSize
+ );
+
+ break;
+ }
+ //
+ // Done if ARP packet was not for us.
+ //
+ DstHwAddr = SrcPrAddr + Private->IpLength;
+ DstPrAddr = DstHwAddr + SnpMode->HwAddressSize;
+
+ if (CompareMem (DstPrAddr, &PxeBcMode->StationIp, Private->IpLength)) {
+ return ;
+ //
+ // not for us
+ //
+ }
+ //
+ // for us - if we did not update entry, add it
+ //
+ if (Index == PxeBcMode->ArpCacheEntries) {
+ //
+ // if we have a full table, get rid of oldest
+ //
+ if (Index == PXE_ARP_CACHE_SIZE) {
+ Index = Private->OldestArpEntry;
+
+ if (++Private->OldestArpEntry == PXE_ARP_CACHE_SIZE) {
+ Private->OldestArpEntry = 0;
+ }
+ } else {
+ ++PxeBcMode->ArpCacheEntries;
+ }
+
+ CopyMem (
+ &PxeBcMode->ArpCache[Index].MacAddr,
+ SrcHwAddr,
+ SnpMode->HwAddressSize
+ );
+
+ CopyMem (
+ &PxeBcMode->ArpCache[Index].IpAddr,
+ SrcPrAddr,
+ Private->IpLength
+ );
+ }
+ //
+ // if this is not a request or we don't yet have an IP, finished
+ //
+ if (ArpPacketPtr->ArpHeader.OpCode != HTONS (ARP_REQUEST) || !Private->GoodStationIp) {
+ return ;
+ }
+ //
+ // Assemble ARP reply.
+ //
+ //
+ // Create media header. [ dest mac | src mac | prot ]
+ //
+ CopyMem (
+ &ArpReplyPacket.MediaHeader[0],
+ SrcHwAddr,
+ SnpMode->HwAddressSize
+ );
+
+ CopyMem (
+ &ArpReplyPacket.MediaHeader[SnpMode->HwAddressSize],
+ &SnpMode->CurrentAddress,
+ SnpMode->HwAddressSize
+ );
+
+ CopyMem (
+ &ArpReplyPacket.MediaHeader[2 * SnpMode->HwAddressSize],
+ &((UINT8 *) MediaHeader)[2 * SnpMode->HwAddressSize],
+ sizeof (UINT16)
+ );
+
+ //
+ // ARP reply header is almost filled in,
+ // just insert the correct opcode.
+ //
+ ArpReplyPacket.ArpHeader.OpCode = HTONS (ARP_REPLY);
+
+ //
+ // Now fill in ARP data. [ src mac | src prot | dest mac | dest prot ]
+ //
+ TmpPtr = ArpReplyPacket.ArpData;
+ CopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize);
+
+ TmpPtr += SnpMode->HwAddressSize;
+ CopyMem (TmpPtr, &PxeBcMode->StationIp, Private->IpLength);
+
+ TmpPtr += Private->IpLength;
+ CopyMem (TmpPtr, SrcHwAddr, SnpMode->HwAddressSize);
+
+ TmpPtr += SnpMode->HwAddressSize;
+ CopyMem (TmpPtr, SrcPrAddr, Private->IpLength);
+
+ //
+ // Now send out the ARP reply.
+ //
+ CopyMem (&TmpMacAddr, SrcHwAddr, sizeof (EFI_MAC_ADDRESS));
+
+ SendPacket (
+ Private,
+ &ArpReplyPacket.MediaHeader,
+ &ArpReplyPacket.ArpHeader,
+ sizeof (ARP_HEADER) + 2 * (Private->IpLength + SnpMode->HwAddressSize),
+ &TmpMacAddr,
+ PXE_PROTOCOL_ETHERNET_ARP,
+ EFI_PXE_BASE_CODE_FUNCTION_ARP
+ );
+
+ //
+ // Give time (100 microseconds) for ARP reply to get onto wire.
+ //
+ gBS->Stall (1000);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return TRUE := If IP address was found and MAC address was stored
+ @return FALSE := If IP address was not found
+
+**/
+BOOLEAN
+GetHwAddr (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN EFI_IP_ADDRESS *ProtocolAddrPtr,
+ OUT EFI_MAC_ADDRESS *HardwareAddrPtr
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ UINTN HardwareAddrLength;
+ UINTN Index;
+
+ PxeBcMode = Private->EfiBc.Mode;
+ HardwareAddrLength = Private->SimpleNetwork->Mode->HwAddressSize;
+
+ for (Index = 0; Index < PxeBcMode->ArpCacheEntries; ++Index) {
+ if (!CompareMem (
+ ProtocolAddrPtr,
+ &PxeBcMode->ArpCache[Index].IpAddr,
+ Private->IpLength
+ )) {
+ CopyMem (
+ HardwareAddrPtr,
+ &PxeBcMode->ArpCache[Index].MacAddr,
+ HardwareAddrLength
+ );
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return EFI_SUCCESS := ARP request sent
+ @return other := ARP request could not be sent
+
+**/
+STATIC
+EFI_STATUS
+SendRequest (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN EFI_IP_ADDRESS *ProtocolAddrPtr,
+ IN EFI_MAC_ADDRESS *HardwareAddrPtr
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ ARP_PACKET *ArpPacket;
+ EFI_STATUS Status;
+ UINTN HardwareAddrLength;
+ UINT8 *SrcProtocolAddrPtr;
+ UINT8 *DestHardwareAddrptr;
+ UINT8 *DestProtocolAddrPtr;
+
+ //
+ //
+ //
+ PxeBcMode = Private->EfiBc.Mode;
+ SnpMode = Private->SimpleNetwork->Mode;
+ HardwareAddrLength = SnpMode->HwAddressSize;
+
+ //
+ // Allocate ARP buffer
+ //
+ if (Private->ArpBuffer == NULL) {
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ SnpMode->MediaHeaderSize + sizeof (ARP_PACKET),
+ &Private->ArpBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ ArpPacket = (VOID *) (Private->ArpBuffer + SnpMode->MediaHeaderSize);
+
+ //
+ // for now, only handle one kind of hw and pr address
+ //
+ ArpPacket->ArpHeader = ArpHeader;
+ ArpPacket->ArpHeader.HwAddLen = (UINT8) HardwareAddrLength;
+ ArpPacket->ArpHeader.ProtAddLen = (UINT8) Private->IpLength;
+
+ //
+ // rest more generic
+ //
+ SrcProtocolAddrPtr = (UINT8 *) (&ArpPacket->SrcHardwareAddr) + HardwareAddrLength;
+ DestHardwareAddrptr = SrcProtocolAddrPtr + Private->IpLength;
+ DestProtocolAddrPtr = DestHardwareAddrptr + HardwareAddrLength;
+
+ CopyMem (DestProtocolAddrPtr, ProtocolAddrPtr, Private->IpLength);
+ CopyMem (DestHardwareAddrptr, HardwareAddrPtr, HardwareAddrLength);
+ CopyMem (SrcProtocolAddrPtr, &PxeBcMode->StationIp, Private->IpLength);
+ CopyMem (
+ &ArpPacket->SrcHardwareAddr,
+ &SnpMode->CurrentAddress,
+ HardwareAddrLength
+ );
+
+ return SendPacket (
+ Private,
+ Private->ArpBuffer,
+ ArpPacket,
+ sizeof (ARP_HEADER) + ((Private->IpLength + HardwareAddrLength) << 1),
+ &SnpMode->BroadcastAddress,
+ PXE_PROTOCOL_ETHERNET_ARP,
+ EFI_PXE_BASE_CODE_FUNCTION_ARP
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// check for address - if not there, send ARP request, wait and check again
+// not how it would be done in a full system
+//
+#define ARP_REQUEST_TIMEOUT_MS 500 // try for half a second
+
+ ////////////////////////////////////////////////////////////
+//
+// BC Arp Routine
+//
+
+/**
+
+
+**/
+EFI_STATUS
+EFIAPI
+BcArp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN EFI_IP_ADDRESS * ProtocolAddrPtr,
+ OUT EFI_MAC_ADDRESS * HardwareAddrPtr OPTIONAL
+ )
+{
+ EFI_MAC_ADDRESS Mac;
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+
+ DEBUG ((DEBUG_INFO, "\nBcArp()"));
+
+ //
+ // Issue BC command
+ //
+ if (ProtocolAddrPtr == NULL) {
+ DEBUG (
+ (DEBUG_INFO,
+ "\nBcArp() Exit #1 %Xh (%r)",
+ EFI_INVALID_PARAMETER,
+ EFI_INVALID_PARAMETER)
+ );
+
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (HardwareAddrPtr == NULL) {
+ HardwareAddrPtr = &Mac;
+ }
+
+ ZeroMem (HardwareAddrPtr, Private->SimpleNetwork->Mode->HwAddressSize);
+
+ if (GetHwAddr (Private, ProtocolAddrPtr, HardwareAddrPtr)) {
+ DEBUG (
+ (DEBUG_INFO,
+ "\nBcArp() Exit #2 %Xh (%r)",
+ EFI_SUCCESS,
+ EFI_SUCCESS)
+ );
+
+ EfiReleaseLock (&Private->Lock);
+ return EFI_SUCCESS;
+ }
+
+ StatCode = DoArp (Private, ProtocolAddrPtr, HardwareAddrPtr);
+
+ DEBUG ((DEBUG_INFO, "\nBcArp() Exit #3 %Xh (%r)", StatCode, StatCode));
+
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return EFI_SUCCESS := MAC address found
+ @return other := MAC address could not be found
+
+**/
+EFI_STATUS
+DoArp (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN EFI_IP_ADDRESS *ProtocolAddrPtr,
+ OUT EFI_MAC_ADDRESS *HardwareAddrPtr
+ )
+{
+ EFI_STATUS StatCode;
+ EFI_EVENT TimeoutEvent;
+ UINTN HeaderSize;
+ UINTN BufferSize;
+ UINT16 Protocol;
+
+ DEBUG ((DEBUG_INFO, "\nDoArp()"));
+
+ //
+ //
+ //
+ StatCode = SendRequest (Private, ProtocolAddrPtr, HardwareAddrPtr);
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG ((DEBUG_INFO, "\nDoArp() Exit #1 %Xh (%r)", StatCode, StatCode));
+ return StatCode;
+ }
+ //
+ //
+ //
+ StatCode = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ return StatCode;
+ }
+
+ StatCode = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ ARP_REQUEST_TIMEOUT_MS * 10000
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return StatCode;
+ }
+ //
+ //
+ //
+ for (;;) {
+ StatCode = WaitForReceive (
+ Private,
+ EFI_PXE_BASE_CODE_FUNCTION_ARP,
+ TimeoutEvent,
+ &HeaderSize,
+ &BufferSize,
+ &Protocol
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ break;
+ }
+
+ if (Protocol != PXE_PROTOCOL_ETHERNET_ARP) {
+ continue;
+ }
+
+ HandleArpReceive (
+ Private,
+ (ARP_PACKET *) (Private->ReceiveBufferPtr + HeaderSize),
+ Private->ReceiveBufferPtr
+ );
+
+ if (GetHwAddr (Private, ProtocolAddrPtr, HardwareAddrPtr)) {
+ break;
+ }
+ }
+
+ DEBUG (
+ (DEBUG_INFO,
+ "\nDoArp() Exit #2 %Xh, (%r)",
+ StatCode,
+ StatCode)
+ );
+
+ gBS->CloseEvent (TimeoutEvent);
+
+ return StatCode;
+}
+
+/* eof - pxe_bc_arp.c */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c
new file mode 100644
index 0000000000..7dec5ec2d2
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c
@@ -0,0 +1,3284 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ pxe_bc_dhcp.c
+
+Abstract:
+ DHCP and PXE discovery protocol implementations.
+
+
+**/
+
+#include "Bc.h"
+
+#include "PxeArch.h"
+
+STATIC EFI_PXE_BASE_CODE_UDP_PORT DhcpServerPort = DHCP_SERVER_PORT;
+STATIC EFI_PXE_BASE_CODE_UDP_PORT DHCPClientPort = DHCP_CLIENT_PORT;
+STATIC EFI_PXE_BASE_CODE_UDP_PORT PseudoDhcpServerPort = PXE_DISCOVERY_PORT;
+#define PSEUDO_DHCP_CLIENT_PORT PseudoDhcpServerPort
+STATIC EFI_IP_ADDRESS BroadcastIP = { 0xffffffff };
+STATIC EFI_IP_ADDRESS DefaultSubnetMask = { 0xffffff00 };
+
+typedef union {
+ DHCPV4_OP_STRUCT *OpPtr;
+ PXE_OP_SERVER_LIST *BootServersStr;
+ PXE_SERVER_LIST *BootServerList;
+ PXE_BOOT_MENU_ENTRY *BootMenuItem;
+ PXE_OP_DISCOVERY_CONTROL *DiscoveryControl;
+ PXE_OP_BOOT_MENU *BootMenu;
+ PXE_OP_BOOT_ITEM *BootItem;
+ DHCPV4_OP_VENDOR_OPTIONS *VendorOptions;
+ DHCPV4_OP_OVERLOAD *Overload;
+ DHCPV4_OP_CLASS *PxeClassStr;
+ DHCPV4_OP_SUBNET_MASK *SubnetMaskStr;
+ DHCPV4_OP_MESSAGE_TYPE *MessageType;
+ UINT8 *BytePtr;
+} UNION_PTR;
+
+#pragma pack(1)
+//
+// option structure for DHCPREQUEST at end of DISCOVER options
+// and for DHCPDECLINE
+//
+STATIC const struct requestopendstr {
+ DHCPV4_OP_REQUESTED_IP OpReqIP;
+ DHCPV4_OP_SERVER_IP DhcServerIpPtr;
+ UINT8 End[1];
+}
+RequestOpEndStr = {
+ {
+ {
+ OP_DHCP_REQ_IP_ADD,
+ DHCPV4_OPTION_LENGTH(DHCPV4_OP_REQUESTED_IP)
+ }
+ },
+ {
+ {
+ OP_DHCP_SERVER_IP,
+ DHCPV4_OPTION_LENGTH(DHCPV4_OP_SERVER_IP)
+ }
+ },
+ {
+ OP_END
+ }
+};
+
+#define DHCP_REQ_OPTIONS (*(struct requestopendstr *) DHCPV4_OPTIONS_BUFFER.End)
+
+PXE_OP_BOOT_ITEM DefaultBootItem = {
+ {
+ VEND_PXE_BOOT_ITEM,
+ DHCPV4_OPTION_LENGTH(PXE_OP_BOOT_ITEM)
+ },
+ 0,
+ 0
+};
+
+//
+// PXE discovery control default structure
+//
+STATIC PXE_OP_DISCOVERY_CONTROL DefaultDisCtl = {
+ { VEND_PXE_DISCOVERY_CONTROL, DHCPV4_OPTION_LENGTH(PXE_OP_DISCOVERY_CONTROL) },
+ 0
+};
+
+//
+// PXE credentials option structure
+//
+typedef struct {
+ UINT8 c[4];
+} PXE_CREDENTIAL;
+
+typedef struct {
+ DHCPV4_OP_HEADER Header;
+ PXE_CREDENTIAL Credentials[1];
+} PXE_OP_CREDENTIAL_TYPES;
+
+//
+// option structure for PXE discover (without credentials)
+//
+typedef struct { // discoveropendstr {
+ DHCPV4_OP_HEADER Header; // vendor options
+ PXE_OP_BOOT_ITEM BootItem;
+ UINT8 End[1]; // if credentials option, it starts here
+} PXE_DISCOVER_OPTIONS;
+
+#define DISCOVERoptions (*(PXE_DISCOVER_OPTIONS *) DHCPV4_OPTIONS_BUFFER.End)
+#define DISCREDoptions (*(PXE_OP_CREDENTIAL_TYPES *) DISCOVERoptions.End)
+
+//
+// common option beginning for all our DHCP messages except
+// DHCPDECLINE and DHCPRELEASE
+//
+STATIC struct optionsstr {
+ UINT8 DhcpCookie[4];
+ DHCPV4_OP_MESSAGE_TYPE DhcpMessageType;
+ DHCPV4_OP_MAX_MESSAGE_SIZE DhcpMaxMessageSize;
+ DHCPV4_OP_REQUESTED_OPTIONS DhcpRequestedOptions;
+ DHCPV4_OP_PLATFORM_ID DhcpPlatformId;
+ DHCPV4_OP_NETWORK_INTERFACE DhcpNetworkInterface;
+ DHCPV4_OP_ARCHITECTURE_TYPE DhcpClientArchitecture;
+ DHCPV4_OP_CLASS_ID DhcpClassIdentifier;
+ UINT8 End[1];
+} DHCPOpStart;
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+VOID
+OptionsStrucInit (
+ VOID
+ )
+{
+ DHCPOpStart.DhcpCookie[0] = 99;
+ DHCPOpStart.DhcpCookie[1] = 130;
+ DHCPOpStart.DhcpCookie[2] = 83;
+ DHCPOpStart.DhcpCookie[3] = 99;
+ DHCPOpStart.DhcpMessageType.Header.OpCode = OP_DHCP_MESSAGE_TYPE;
+ DHCPOpStart.DhcpMessageType.Header.Length = 1;
+ DHCPOpStart.DhcpMessageType.Type = DHCPDISCOVER;
+ DHCPOpStart.DhcpMaxMessageSize.Header.OpCode = OP_DHCP_MAX_MESSAGE_SZ;
+ DHCPOpStart.DhcpMaxMessageSize.Header.Length = 2;
+ DHCPOpStart.DhcpMaxMessageSize.MaxSize[0] = MAX_DHCP_MSG_SZ >> 8;
+ DHCPOpStart.DhcpMaxMessageSize.MaxSize[1] = MAX_DHCP_MSG_SZ & 0xff;
+ DHCPOpStart.DhcpRequestedOptions.Header.OpCode = OP_DHCP_PARM_REQ_LIST;
+ DHCPOpStart.DhcpRequestedOptions.Header.Length = sizeof (DHCPV4_REQUESTED_OPTIONS_DATA);
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_SUBNET_MASK = OP_SUBNET_MASK; /* 1 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_OFFSET = OP_TIME_OFFSET; /* 2 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_ROUTER_LIST = OP_ROUTER_LIST; /* 3 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_SERVERS = OP_TIME_SERVERS; /* 4 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_NAME_SERVERS = OP_NAME_SERVERS; /* 5 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DNS_SERVERS = OP_DNS_SERVERS; /* 6 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_HOST_NAME = OP_HOST_NAME; /* 12 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_BOOT_FILE_SZ = OP_BOOT_FILE_SZ; /* 13 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DOMAIN_NAME = OP_DOMAIN_NAME; /* 15 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_ROOT_PATH = OP_ROOT_PATH; /* 17 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_EXTENSION_PATH = OP_EXTENSION_PATH; /* 18 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_MAX_DATAGRAM_SZ = OP_MAX_DATAGRAM_SZ; /* 22 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DEFAULT_TTL = OP_DEFAULT_TTL; /* 23 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_BROADCAST_ADD = OP_BROADCAST_ADD; /* 28 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_DOMAIN_NAME = OP_NIS_DOMAIN_NAME; /* 40 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_SERVERS = OP_NIS_SERVERS; /* 41 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_NTP_SERVERS = OP_NTP_SERVERS; /* 42 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_VENDOR_SPECIFIC = OP_VENDOR_SPECIFIC; /* 43 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REQ_IP_ADD = OP_DHCP_REQ_IP_ADD; /* 50 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_LEASE_TIME = OP_DHCP_LEASE_TIME; /* 51 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_SERVER_IP = OP_DHCP_SERVER_IP; /* 54 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_RENEWAL_TIME = OP_DHCP_RENEWAL_TIME; /* 58 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REBINDING_TIME = OP_DHCP_REBINDING_TIME; /* 59 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_CLASS_IDENTIFIER = OP_DHCP_CLASS_IDENTIFIER; /* 60 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_TFTP_SERVER_NAME = OP_DHCP_TFTP_SERVER_NAME; /* 66 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_BOOTFILE = OP_DHCP_BOOTFILE; /* 67 */
+ DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_PLATFORM_ID = OP_DHCP_PLATFORM_ID; /* 97 */
+ DHCPOpStart.DhcpRequestedOptions.Data.VendorOption128 = 128;
+ DHCPOpStart.DhcpRequestedOptions.Data.VendorOption129 = 129;
+ DHCPOpStart.DhcpRequestedOptions.Data.VendorOption130 = 130;
+ DHCPOpStart.DhcpRequestedOptions.Data.VendorOption131 = 131;
+ DHCPOpStart.DhcpRequestedOptions.Data.VendorOption132 = 132;
+ DHCPOpStart.DhcpRequestedOptions.Data.VendorOption133 = 133, DHCPOpStart.DhcpRequestedOptions.Data.VendorOption134 = 134;
+ DHCPOpStart.DhcpRequestedOptions.Data.VendorOption135 = 135;
+ DHCPOpStart.DhcpPlatformId.Header.OpCode = OP_DHCP_PLATFORM_ID;
+ DHCPOpStart.DhcpPlatformId.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_PLATFORM_ID);
+ DHCPOpStart.DhcpNetworkInterface.Header.OpCode = OP_DHCP_NETWORK_ARCH;
+ DHCPOpStart.DhcpNetworkInterface.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_NETWORK_INTERFACE);
+ DHCPOpStart.DhcpNetworkInterface.Type = 0;
+ DHCPOpStart.DhcpNetworkInterface.MajorVersion = 0;
+ DHCPOpStart.DhcpNetworkInterface.MinorVersion = 0;
+ DHCPOpStart.DhcpClientArchitecture.Header.OpCode = OP_DHCP_SYSTEM_ARCH;
+ DHCPOpStart.DhcpClientArchitecture.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_ARCHITECTURE_TYPE);
+ DHCPOpStart.DhcpClientArchitecture.Type = HTONS (SYS_ARCH);
+ DHCPOpStart.DhcpClassIdentifier.Header.OpCode = OP_DHCP_CLASS_IDENTIFIER;
+ DHCPOpStart.DhcpClassIdentifier.Header.Length = sizeof (DHCPV4_CLASS_ID_DATA);
+ CopyMem (
+ DHCPOpStart.DhcpClassIdentifier.Data.ClassIdentifier,
+ "PXEClient:",
+ sizeof ("PXEClient:")
+ );
+ CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit2, "Arch:", sizeof ("Arch:"));
+ CopyMem (
+ DHCPOpStart.DhcpClassIdentifier.Data.ArchitectureType,
+ "xxxxx",
+ sizeof ("xxxxx")
+ );
+ CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit3, ":", sizeof (":"));
+ CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.InterfaceName, "XXXX", sizeof ("XXXX"));
+ CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit4, ":", sizeof (":"));
+ CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMajor, "yyy", sizeof ("yyy"));
+ CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMinor, "xxx", sizeof ("xxx"));
+ DHCPOpStart.End[0] = OP_END;
+};
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// DHCPDECLINE option structure
+//
+struct opdeclinestr {
+ UINT8 DhcpCookie[4];
+ DHCPV4_OP_MESSAGE_TYPE DhcpMessageType;
+ struct requestopendstr OpDeclineEnd;
+};
+
+#define DHCPDECLINEoptions (*(struct opdeclinestr *) DHCPV4_TRANSMIT_BUFFER.options)
+
+//
+// DHCPRELEASE option structure
+//
+struct opreleasestr {
+ UINT8 DhcpCookie[4];
+ DHCPV4_OP_MESSAGE_TYPE DhcpMessageType;
+ DHCPV4_OP_SERVER_IP DhcServerIpPtr;
+ UINT8 End[1];
+};
+
+#define DHCPRELEASEoptions (*(struct opreleasestr *) DHCPV4_TRANSMIT_BUFFER.options)
+
+//
+// array of PXE vendor options in which we are interested
+// value 0 -> not of interest, else value is index into PXE OPTION array
+// option values from 1 to MAX_OUR_PXE_OPT
+//
+STATIC UINT8 ourPXEopts[MAX_OUR_PXE_OPT] = {
+ VEND_PXE_MTFTP_IP_IX, // multicast IP address of bootfile for MTFTP listen
+ VEND_PXE_MTFTP_CPORT_IX, // UDP Port to monitor for MTFTP responses - Intel order
+ VEND_PXE_MTFTP_SPORT_IX, // Server UDP Port for MTFTP open - Intel order
+ VEND_PXE_MTFTP_TMOUT_IX, // Listen timeout - secs
+ VEND_PXE_MTFTP_DELAY_IX, // Transmission timeout - secs
+ VEND_PXE_DISCOVERY_CONTROL_IX, // bit field
+ VEND_PXE_DISCOVERY_MCAST_ADDR_IX, // boot server discovery multicast address
+ VEND_PXE_BOOT_SERVERS_IX, // list of boot servers of form tp(2) cnt(1) ips[cnt]
+ VEND_PXE_BOOT_MENU_IX,
+ VEND_PXE_BOOT_PROMPT_IX,
+ VEND_PXE_MCAST_ADDRS_ALLOC_IX, // not used by client
+ VEND_PXE_CREDENTIAL_TYPES_IX,
+ VEND_13_IX, // not used by client
+ VEND_14_IX, // not used by client
+ VEND_15_IX, // not used by client
+ VEND_16_IX, // not used by client
+ VEND_17_IX, // not used by client
+ VEND_18_IX, // not used by client
+ VEND_19_IX, // not used by client
+ VEND_20_IX, // not used by client
+ VEND_21_IX, // not used by client
+ VEND_22_IX, // not used by client
+ VEND_23_IX, // not used by client
+ VEND_24_IX, // not used by client
+ VEND_25_IX, // not used by client
+ VEND_26_IX, // not used by client
+ VEND_27_IX, // not used by client
+ VEND_28_IX, // not used by client
+ VEND_29_IX, // not used by client
+ VEND_30_IX, // not used by client
+ VEND_31_IX, // not used by client
+ VEND_32_IX, // not used by client
+ VEND_33_IX, // not used by client
+ VEND_34_IX, // not used by client
+ VEND_35_IX, // not used by client
+ VEND_36_IX, // not used by client
+ VEND_37_IX, // not used by client
+ VEND_38_IX, // not used by client
+ VEND_39_IX, // not used by client
+ VEND_40_IX, // not used by client
+ VEND_41_IX, // not used by client
+ VEND_42_IX, // not used by client
+ VEND_43_IX, // not used by client
+ VEND_44_IX, // not used by client
+ VEND_45_IX, // not used by client
+ VEND_46_IX, // not used by client
+ VEND_47_IX, // not used by client
+ VEND_48_IX, // not used by client
+ VEND_49_IX, // not used by client
+ VEND_50_IX, // not used by client
+ VEND_51_IX, // not used by client
+ VEND_52_IX, // not used by client
+ VEND_53_IX, // not used by client
+ VEND_54_IX, // not used by client
+ VEND_55_IX, // not used by client
+ VEND_56_IX, // not used by client
+ VEND_57_IX, // not used by client
+ VEND_58_IX, // not used by client
+ VEND_59_IX, // not used by client
+ VEND_60_IX, // not used by client
+ VEND_61_IX, // not used by client
+ VEND_62_IX, // not used by client
+ VEND_63_IX, // not used by client
+ VEND_64_IX, // not used by client
+ VEND_65_IX, // not used by client
+ VEND_66_IX, // not used by client
+ VEND_67_IX, // not used by client
+ VEND_68_IX, // not used by client
+ VEND_69_IX, // not used by client
+ VEND_70_IX, // not used by client
+ VEND_PXE_BOOT_ITEM_IX
+};
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// array of options in which we are interested
+// value 0 -> not of interest, else value is index into OPTION array
+// option values from 1 to MAX_OUR_OPT
+//
+STATIC UINT8 OurDhcpOptions[MAX_OUR_OPT] = {
+ OP_SUBNET_MASK_IX, // OP_SUBNET_MASK 1 // data is the subnet mask
+ OP_TIME_OFFSET_IX, // OP_TIME_OFFSET 2 // data is the time offset of subnet to UTC in seconds
+ OP_ROUTER_LIST_IX, // OP_ROUTER_LIST 3 // list of routers on subnet
+ OP_TIME_SERVERS_IX, // OP_TIME_SERVERS 4 // list of time servers available
+ OP_NAME_SERVERS_IX, // OP_NAME_SERVERS 5 // list of name servers available
+ OP_DNS_SERVERS_IX, // OP_DNS_SERVERS 6 // list of DNS servers available
+ OP_LOG_SERVERS_IX, // OP_LOG_SERVERS 7
+ OP_COOKIE_SERVERS_IX, // OP_COOKIE_SERVERS 8
+ OP_LPR_SREVERS_IX, // OP_LPR_SREVERS 9
+ OP_IMPRESS_SERVERS_IX, // OP_IMPRESS_SERVERS 10
+ OP_RES_LOC_SERVERS_IX, // OP_RES_LOC_SERVERS 11
+ OP_HOST_NAME_IX, // OP_HOST_NAME 12 // client name
+ OP_BOOT_FILE_SZ_IX, // OP_BOOT_FILE_SZ 13 // number of 512 blocks of boot file
+ OP_DUMP_FILE_IX, // OP_DUMP_FILE 14 // path name of dump file if client crashes
+ OP_DOMAIN_NAME_IX, // OP_DOMAIN_NAME 15 // domain name to use
+ OP_SWAP_SERVER_IX, // OP_SWAP_SERVER 16
+ OP_ROOT_PATH_IX, // OP_ROOT_PATH 17 // path name containing root disk
+ OP_EXTENSION_PATH_IX, // OP_EXTENSION_PATH 18 // name of TFTP downloadable file of form of OP
+ OP_IP_FORWARDING_IX, // OP_IP_FORWARDING 19 // enable/disable IP packet forwarding
+ OP_NON_LOCAL_SRC_RTE_IX, // OP_NON_LOCAL_SRC_RTE 20 // enable/disable non local source routing
+ OP_POLICY_FILTER_IX, // OP_POLICY_FILTER 21 // policy filters for non local source routing
+ OP_MAX_DATAGRAM_SZ_IX, // OP_MAX_DATAGRAM_SZ 22 // maximum datagram reassembly size
+ OP_DEFAULT_TTL_IX, // OP_DEFAULT_TTL 23 // default IP time to live
+ OP_MTU_AGING_TIMEOUT_IX, // OP_MTU_AGING_TIMEOUT 24
+ OP_MTU_SIZES_IX, // OP_MTU_SIZES 25
+ OP_MTU_TO_USE_IX, // OP_MTU_TO_USE 26
+ OP_ALL_SUBNETS_LOCAL_IX, // OP_ALL_SUBNETS_LOCAL 27
+ OP_BROADCAST_ADD_IX, // OP_BROADCAST_ADD 28 // broadcast address used on subnet
+ OP_PERFORM_MASK_DISCOVERY_IX, // OP_PERFORM_MASK_DISCOVERY 29 // perform mask discovery using ICMP
+ OP_RESPOND_TO_MASK_REQ_IX, // OP_RESPOND_TO_MASK_REQ 30 // respond to subnet mask requests using ICMP
+ OP_PERFORM_ROUTER_DISCOVERY_IX, // OP_PERFORM_ROUTER_DISCOVERY 31
+ OP_ROUTER_SOLICIT_ADDRESS_IX, // OP_ROUTER_SOLICIT_ADDRESS 32
+ OP_STATIC_ROUTER_LIST_IX, // OP_STATIC_ROUTER_LIST 33 // list of dest/route pairs
+ OP_USE_ARP_TRAILERS_IX, // OP_USE_ARP_TRAILERS 34
+ OP_ARP_CACHE_TIMEOUT_IX, // OP_ARP_CACHE_TIMEOUT 35
+ OP_ETHERNET_ENCAPSULATION_IX, // OP_ETHERNET_ENCAPSULATION 36 // 0 -> RFC 894, 1 -> IEEE 802.3 (RFC 1042)
+ OP_TCP_DEFAULT_TTL_IX, // OP_TCP_DEFAULT_TTL 37 // default time to live when sending TCP segments
+ OP_TCP_KEEP_ALIVE_INT_IX, // OP_TCP_KEEP_ALIVE_INT 38 // keep alive interval in seconds
+ OP_KEEP_ALIVE_GARBAGE_IX, // OP_KEEP_ALIVE_GARBAGE 39
+ OP_NIS_DOMAIN_NAME_IX, // OP_NIS_DOMAIN_NAME 40
+ OP_NIS_SERVERS_IX, // OP_NIS_SERVERS 41
+ OP_NTP_SERVERS_IX, // OP_NTP_SERVERS 42
+ OP_VENDOR_SPECIFIC_IX, // OP_VENDOR_SPECIFIC 43
+ OP_NBNS_SERVERS_IX, // OP_NBNS_SERVERS 44
+ OP_NBDD_SERVERS_IX, // OP_NBDD_SERVERS 45
+ OP_NETBIOS_NODE_TYPE_IX, // OP_NETBIOS_NODE_TYPE 46
+ OP_NETBIOS_SCOPE_IX, // OP_NETBIOS_SCOPE 47
+ OP_XWINDOW_SYSTEM_FONT_SERVERS_IX, // OP_XWINDOW_SYSTEM_FONT_SERVERS 48
+ OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS_IX, // OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49
+ OP_DHCP_REQ_IP_ADD_IX, // OP_DHCP_REQ_IP_ADD 50 // requested IP address - in DHCPDISCOVER
+ OP_DHCP_LEASE_TIME_IX, // OP_DHCP_LEASE_TIME 51 // lease time requested/granted
+ OP_DHCP_OPTION_OVERLOAD_IX, // OP_DHCP_OPTION_OVERLOAD 52 // file/server name/both used to hold options
+ OP_DHCP_MESSAGE_TYPE_IX, // OP_DHCP_MESSAGE_TYPE 53 // message type
+ OP_DHCP_SERVER_IP_IX, // OP_DHCP_SERVER_IP 54 // IP of server
+ OP_DHCP_PARM_REQ_LIST_IX, // OP_DHCP_PARM_REQ_LIST 55 // list of requested parameters
+ OP_DHCP_ERROR_MESSAGE_IX, // OP_DHCP_ERROR_MESSAGE 56 // in DHCPNAK or DECLINE messages
+ OP_DHCP_MAX_MESSAGE_SZ_IX, // OP_DHCP_MAX_MESSAGE_SZ 57 // maximum DHCP message size client will accept
+ OP_DHCP_RENEWAL_TIME_IX, // OP_DHCP_RENEWAL_TIME 58 // time in seconds before transitioning to RENEWING state
+ OP_DHCP_REBINDING_TIME_IX, // OP_DHCP_REBINDING_TIME 59 // time in seconds before transitioning to REBINDING state
+ OP_DHCP_CLASS_IDENTIFIER_IX, // OP_DHCP_CLASS_IDENTIFIER 60
+ OP_DHCP_CLIENT_IDENTIFIER_IX, // OP_DHCP_CLIENT_IDENTIFIER 61
+ OP_RESERVED62_IX, // OP_RESERVED62
+ OP_RESERVED63_IX, // OP_RESERVED63
+ OP_NISPLUS_DOMAIN_NAME_IX, // OP_NISPLUS_DOMAIN_NAME 64
+ OP_NISPLUS_SERVERS_IX, // OP_NISPLUS_SERVERS 65
+ OP_DHCP_TFTP_SERVER_NAME_IX, // OP_DHCP_TFTP_SERVER_NAME 66
+ OP_DHCP_BOOTFILE_IX // OP_DHCP_BOOTFILE 67
+};
+
+#define RxBuf ((DHCP_RECEIVE_BUFFER *) (Private->ReceiveBuffers))
+
+#pragma pack()
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @param Smbios Pointer to SMBIOS structure
+ @param StringNumber String number to return. 0 is used to skip all
+ strings and point to the next SMBIOS structure.
+
+ @return Pointer to string, or pointer to next SMBIOS strcuture if StringNumber == 0
+
+**/
+CHAR8 *
+PxeBcLibGetSmbiosString (
+ IN SMBIOS_STRUCTURE_POINTER *Smbios,
+ IN UINT16 StringNumber
+ )
+{
+ UINT16 Index;
+ CHAR8 *String;
+
+ //
+ // Skip over formatted section
+ //
+ String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);
+
+ //
+ // Look through unformated section
+ //
+ for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {
+ if (StringNumber == Index) {
+ return String;
+ }
+ //
+ // Skip string
+ //
+ for (; *String != 0; String++)
+ ;
+ String++;
+
+ if (*String == 0) {
+ //
+ // If double NULL then we are done.
+ // Return pointer to next structure in Smbios.
+ // if you pass in a 0 you will always get here
+ //
+ Smbios->Raw = (UINT8 *)++String;
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ This function gets system guid and serial number from the smbios table
+
+ @param SystemGuid The pointer of returned system guid
+ @param SystemSerialNumber The pointer of returned system serial number
+
+ @retval EFI_SUCCESS Successfully get the system guid and system serial
+ number
+ @retval EFI_NOT_FOUND Not find the SMBIOS table
+
+**/
+EFI_STATUS
+PxeBcLibGetSmbiosSystemGuidAndSerialNumber (
+ IN EFI_GUID *SystemGuid,
+ OUT CHAR8 **SystemSerialNumber
+ )
+{
+ EFI_STATUS Status;
+ SMBIOS_STRUCTURE_TABLE *SmbiosTable;
+ SMBIOS_STRUCTURE_POINTER Smbios;
+ SMBIOS_STRUCTURE_POINTER SmbiosEnd;
+ UINT16 Index;
+
+ Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ Smbios.Hdr = (SMBIOS_HEADER *) (UINTN) SmbiosTable->TableAddress;
+ SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);
+
+ for (Index = 0; Index < SmbiosTable->TableLength; Index++) {
+ if (Smbios.Hdr->Type == 1) {
+ if (Smbios.Hdr->Length < 0x19) {
+ //
+ // Older version did not support Guid and Serial number
+ //
+ continue;
+ }
+ //
+ // SMBIOS tables are byte packed so we need to do a byte copy to
+ // prevend alignment faults on Itanium-based platform.
+ //
+ CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));
+ *SystemSerialNumber = PxeBcLibGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);
+
+ return EFI_SUCCESS;
+ }
+ //
+ // Make Smbios point to the next record
+ //
+ PxeBcLibGetSmbiosString (&Smbios, 0);
+
+ if (Smbios.Raw >= SmbiosEnd.Raw) {
+ //
+ // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.
+ // given this we must double check against the lenght of
+ // the structure.
+ //
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// add router list to list
+//
+STATIC
+VOID
+Ip4AddRouterList (
+ PXE_BASECODE_DEVICE *Private,
+ DHCPV4_OP_IP_LIST *IpListPtr
+ )
+{
+ EFI_IP_ADDRESS TmpIp;
+ INTN Index;
+ INTN num;
+
+ if (IpListPtr == NULL) {
+ return ;
+ }
+
+ for (Index = 0, num = IpListPtr->Header.Length >> 2; Index < num; ++Index) {
+ CopyMem (&TmpIp, &IpListPtr->IpList[Index], 4);
+ Ip4AddRouter (Private, &TmpIp);
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// send ARP for our IP - fail if someone has it
+//
+STATIC
+BOOLEAN
+SetStationIP (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ EFI_MAC_ADDRESS DestMac;
+ EFI_STATUS EfiStatus;
+
+ ZeroMem (&DestMac, sizeof DestMac);
+
+ if (GetHwAddr(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac)
+ || DoArp(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac) == EFI_SUCCESS) {
+ return FALSE; // somebody else has this IP
+ }
+
+ CopyMem (
+ (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->StationIp,
+ &DHCP_REQ_OPTIONS.OpReqIP.Ip,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ Private->GoodStationIp = TRUE;
+
+ if (!Private->UseIgmpv1Reporting) {
+ return TRUE;
+ }
+
+ if (Private->Igmpv1TimeoutEvent != NULL) {
+ return TRUE;
+ }
+
+ EfiStatus = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Private->Igmpv1TimeoutEvent
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ Private->Igmpv1TimeoutEvent = NULL;
+ return TRUE;
+ }
+
+ EfiStatus = gBS->SetTimer (
+ Private->Igmpv1TimeoutEvent,
+ TimerRelative,
+ (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000
+ ); /* 400 seconds */
+
+ if (EFI_ERROR (EfiStatus)) {
+ gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
+ Private->Igmpv1TimeoutEvent = NULL;
+ }
+
+ return TRUE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+VOID
+AddRouters (
+ PXE_BASECODE_DEVICE *Private,
+ DHCP_RECEIVE_BUFFER *RxBufPtr
+ )
+{
+ Ip4AddRouterList (
+ Private,
+ (DHCPV4_OP_IP_LIST *) RxBufPtr->OpAdds.PktOptAdds[OP_ROUTER_LIST_IX - 1]
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+EFI_STATUS
+DoUdpWrite (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
+ EFI_IP_ADDRESS *ClientIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ClientPortPtr
+ )
+{
+ UINTN Len;
+
+ Len = sizeof DHCPV4_TRANSMIT_BUFFER;
+
+ return UdpWrite (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
+ ServerIpPtr,
+ ServerPortPtr,
+ 0,
+ ClientIpPtr,
+ ClientPortPtr,
+ 0,
+ 0,
+ &Len,
+ Private->TransmitBuffer
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// initialize the DHCP structure
+//
+typedef struct {
+ UINT8 x[4];
+} C4Str;
+
+STATIC
+VOID
+InitDhcpv4TxBuf (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ UINTN HwAddrLen;
+ UINT8 *String;
+ CHAR8 *SystemSerialNumber;
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+
+ PxebcMode = Private->EfiBc.Mode;
+
+ ZeroMem (&DHCPV4_TRANSMIT_BUFFER, sizeof (DHCPV4_STRUCT));
+ DHCPV4_TRANSMIT_BUFFER.op = BOOTP_REQUEST;
+ DHCPV4_TRANSMIT_BUFFER.htype = Private->SimpleNetwork->Mode->IfType;
+ DHCPV4_TRANSMIT_BUFFER.flags = HTONS (DHCP_BROADCAST_FLAG);
+ CopyMem (&DHCPV4_OPTIONS_BUFFER, (VOID *) &DHCPOpStart, sizeof (DHCPOpStart));
+
+ //
+ // default to hardware address
+ //
+ HwAddrLen = Private->SimpleNetwork->Mode->HwAddressSize;
+
+ if (HwAddrLen > sizeof DHCPV4_TRANSMIT_BUFFER.chaddr) {
+ HwAddrLen = sizeof DHCPV4_TRANSMIT_BUFFER.chaddr;
+ }
+
+ String = (UINT8 *) &Private->SimpleNetwork->Mode->CurrentAddress;
+
+ if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber (
+ (EFI_GUID *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid,
+ &SystemSerialNumber
+ ) == EFI_SUCCESS) {
+ if (PxebcMode->SendGUID) {
+ HwAddrLen = sizeof (EFI_GUID);
+ String = (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid;
+ }
+ } else {
+ //
+ // GUID not yet set - send all 0xff's to show programable (via SetVariable)
+ // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);
+ // GUID not yet set - send all 0's to show not programable
+ //
+ ZeroMem (DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof (EFI_GUID));
+ }
+
+ DHCPV4_TRANSMIT_BUFFER.hlen = (UINT8) HwAddrLen;
+ CopyMem (DHCPV4_TRANSMIT_BUFFER.chaddr, String, HwAddrLen);
+
+ CvtNum (
+ SYS_ARCH,
+ (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType,
+ sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType
+ );
+
+ DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.Type = Private->NiiPtr->Type;
+ DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion = Private->NiiPtr->MajorVer;
+ DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion = Private->NiiPtr->MinorVer;
+
+ *(C4Str *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.InterfaceName = *(C4Str *) Private->NiiPtr->StringId;
+
+ CvtNum (
+ DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion,
+ (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor,
+ sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor
+ );
+
+ CvtNum (
+ DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion,
+ (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor,
+ sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+UINT32
+DecodePxeOptions (
+ DHCP_RECEIVE_BUFFER *RxBufPtr,
+ UINT8 *ptr,
+ INTN Len
+ )
+{
+ UINT8 Op;
+ UINT8 *EndPtr;
+ INTN Index;
+ UNION_PTR LocalPtr;
+ UINT32 status;
+
+ status = 0;
+
+ for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) {
+ Op = ptr[0];
+ Len = ptr[1];
+
+ switch (Op) {
+ case OP_PAD:
+ Len = -1;
+ break;
+
+ case OP_END:
+ return status;
+
+ default:
+ LocalPtr.BytePtr = ptr;
+ if (Op <= MAX_OUR_PXE_OPT) {
+ Index = ourPXEopts[Op - 1];
+ if (Index) {
+ RxBufPtr->OpAdds.PxeOptAdds[Index - 1] = LocalPtr.OpPtr;
+ status |= 1 << Index;
+ if (Index == VEND_PXE_BOOT_ITEM && LocalPtr.BootItem->Header.Length == 3) {
+ RxBufPtr->OpAdds.Status |= USE_THREE_BYTE;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return status;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+VOID
+DecodeOptions (
+ DHCP_RECEIVE_BUFFER *RxBufPtr,
+ UINT8 *ptr,
+ INTN Len
+ )
+{
+ UINT8 Op;
+ UINT8 *EndPtr;
+ INTN Index;
+ UNION_PTR LocalPtr;
+
+ for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) {
+ Op = ptr[0];
+ Len = ptr[1];
+
+ switch (Op) {
+ case OP_PAD:
+ Len = -1;
+ break;
+
+ case OP_END:
+ return ;
+
+ default:
+ LocalPtr.BytePtr = ptr;
+ if (Op <= MAX_OUR_OPT) {
+ Index = OurDhcpOptions[Op - 1];
+ if (Index) {
+ RxBufPtr->OpAdds.PktOptAdds[Index - 1] = LocalPtr.OpPtr;
+ if (Index == OP_VENDOR_SPECIFIC_IX) {
+ UINT32 status;
+ status = DecodePxeOptions (
+ RxBufPtr,
+ (UINT8 *) LocalPtr.VendorOptions->VendorOptions,
+ LocalPtr.VendorOptions->Header.Length
+ );
+ if (status) {
+ RxBufPtr->OpAdds.Status |= PXE_TYPE;
+ //
+ // check for all the MTFTP info options present - any missing is a nogo
+ //
+ if ((status & WfM11a_OPTS) == WfM11a_OPTS) {
+ RxBufPtr->OpAdds.Status |= WfM11a_TYPE;
+ }
+
+ if (status & DISCOVER_OPTS) {
+ RxBufPtr->OpAdds.Status |= DISCOVER_TYPE;
+ }
+
+ if (status & CREDENTIALS_OPT) {
+ RxBufPtr->OpAdds.Status |= CREDENTIALS_TYPE;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+VOID
+Parse (
+ DHCP_RECEIVE_BUFFER *RxBufPtr,
+ INTN Len
+ )
+{
+ UNION_PTR LocalPtr;
+
+ //
+ // initialize
+ //
+ SetMem (&RxBufPtr->OpAdds, sizeof RxBufPtr->OpAdds, 0);
+
+ DecodeOptions (
+ RxBufPtr,
+ RxBufPtr->u.Dhcpv4.options + 4,
+ Len - (sizeof RxBufPtr->u.Dhcpv4 - sizeof RxBufPtr->u.Dhcpv4.options + 4)
+ );
+
+ LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_OPTION_OVERLOAD_IX - 1];
+
+ if ((LocalPtr.OpPtr) && (LocalPtr.Overload->Overload & OVLD_SRVR_NAME)) {
+ DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.sname, sizeof RxBufPtr->u.Dhcpv4.sname);
+ }
+
+ if (LocalPtr.OpPtr && (LocalPtr.Overload->Overload & OVLD_FILE)) {
+ DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.file, sizeof RxBufPtr->u.Dhcpv4.file);
+ } else if (!RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && RxBufPtr->u.Dhcpv4.file[0]) {
+ RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] = (DHCPV4_OP_STRUCT *) (RxBufPtr->u.Dhcpv4.file - sizeof (DHCPV4_OP_HEADER));
+
+ RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length = (UINT8) AsciiStrLen (RxBufPtr->u.Dhcpv4.file);
+ }
+
+ LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_CLASS_IDENTIFIER_IX - 1];
+
+ if ((LocalPtr.OpPtr) &&
+ LocalPtr.PxeClassStr->Header.Length >= 9 &&
+ !CompareMem (LocalPtr.PxeClassStr->Class, "PXEClient", 9)
+ ) {
+ RxBufPtr->OpAdds.Status |= PXE_TYPE;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+VOID
+CopyParseRxBuf (
+ PXE_BASECODE_DEVICE *Private,
+ INTN RxBufIndex,
+ INTN PacketIndex
+ )
+{
+ DHCP_RECEIVE_BUFFER *RxBufPtr;
+
+ RxBufPtr = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[PacketIndex];
+
+ CopyMem (
+ &RxBufPtr->u.Dhcpv4,
+ &RxBuf[RxBufIndex].u.Dhcpv4,
+ sizeof (RxBuf[RxBufIndex].u.Dhcpv4)
+ );
+
+ Parse (RxBufPtr, sizeof RxBufPtr->u.ReceiveBuffer);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+VOID
+CopyProxyRxBuf (
+ PXE_BASECODE_DEVICE *Private,
+ INTN RxBufIndex
+ )
+{
+ Private->EfiBc.Mode->ProxyOfferReceived = TRUE;
+ CopyParseRxBuf (Private, RxBufIndex, PXE_OFFER_INDEX);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+VOID
+CopyParse (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_PXE_BASE_CODE_PACKET *PacketPtr,
+ EFI_PXE_BASE_CODE_PACKET *NewPacketPtr,
+ INTN Index
+ )
+{
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf;
+
+ DhcpRxBuf = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[Index];
+
+ CopyMem (
+ (EFI_PXE_BASE_CODE_PACKET *) &DhcpRxBuf->u.Dhcpv4,
+ NewPacketPtr,
+ sizeof (*NewPacketPtr)
+ );
+
+ CopyMem (&*PacketPtr, &*NewPacketPtr, sizeof (*NewPacketPtr));
+
+ Parse (DhcpRxBuf, sizeof DhcpRxBuf->u.ReceiveBuffer);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+BOOLEAN
+AckEdit (
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf
+ )
+{
+ UNION_PTR LocalPtr;
+
+ LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1];
+
+ //
+ // check that an ACK
+ // if a DHCP type, must be DHCPOFFER and must have server id
+ //
+ return (BOOLEAN)
+ (
+ (LocalPtr.OpPtr) &&
+ (LocalPtr.MessageType->Type == DHCPACK) &&
+ DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// if a discover type packet, make sure all required fields are present
+//
+BOOLEAN
+DHCPOfferAckEdit (
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf
+ )
+{
+ PXE_OP_SERVER_LIST *BootServerOpPtr;
+ UNION_PTR LocalPtr;
+
+ if ((DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) == 0) {
+ return TRUE;
+ }
+
+ LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];
+
+ if (LocalPtr.OpPtr == NULL) {
+ LocalPtr.OpPtr = (DHCPV4_OP_STRUCT *) &DefaultDisCtl;
+ DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultDisCtl;
+ }
+ //
+ // make sure all required fields are here
+ // if mucticast enabled, need multicast address
+ //
+ if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST) &&
+ (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1] || !IS_MULTICAST (((DHCPV4_OP_STRUCT *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Data))
+ ) {
+ return FALSE;
+ //
+ // missing required field
+ //
+ }
+ //
+ // if a list, it better be good
+ //
+ BootServerOpPtr = (PXE_OP_SERVER_LIST *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1];
+
+ if (BootServerOpPtr != NULL) {
+ PXE_SERVER_LIST *BootServerListPtr;
+ INTN ServerListLen;
+ INTN ServerEntryLen;
+
+ BootServerListPtr = BootServerOpPtr->ServerList;
+ ServerListLen = BootServerOpPtr->Header.Length;
+
+ do {
+ EFI_IPv4_ADDRESS *IpListPtr;
+ INTN IpCnt;
+
+ IpCnt = BootServerListPtr->u.Ipv4List.IpCount;
+
+ ServerEntryLen = sizeof (PXEV4_SERVER_LIST) + 2 + (IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS);
+
+ if (ServerListLen < ServerEntryLen) {
+ //
+ // missing required field
+ //
+ return FALSE;
+ }
+
+ IpListPtr = BootServerListPtr->u.Ipv4List.IpList;
+
+ while (IpCnt--) {
+ if (IS_MULTICAST (IpListPtr)) {
+ //
+ // missing required field
+ //
+ return FALSE;
+ } else {
+ ++IpListPtr;
+ }
+ }
+
+ BootServerListPtr = (PXE_SERVER_LIST *) IpListPtr;
+ } while (ServerListLen -= ServerEntryLen);
+ }
+ //
+ // else there must be a list if use list enabled or multicast and
+ // broadcast disabled
+ //
+ else if ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) ||
+ ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST))
+ ) {
+ //
+ // missing required field
+ //
+ return FALSE;
+ }
+ //
+ // if not USE_BOOTFILE or no bootfile given, must have menu stuff
+ //
+ if (!(LocalPtr.DiscoveryControl->ControlBits & USE_BOOTFILE) ||
+ !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]
+ ) {
+ INTN MenuLth;
+
+ LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];
+
+ if (LocalPtr.OpPtr == NULL || !DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]) {
+ //
+ // missing required field
+ //
+ return FALSE;
+ }
+ //
+ // make sure menu valid
+ //
+ MenuLth = LocalPtr.BootMenu->Header.Length;
+ LocalPtr.BootMenuItem = LocalPtr.BootMenu->MenuItem;
+
+ do {
+ INTN MenuItemLen;
+
+ MenuItemLen = LocalPtr.BootMenuItem->DataLen;
+
+ if (MenuItemLen == 0) {
+ //
+ // missing required field
+ //
+ return FALSE;
+ }
+
+ MenuItemLen += sizeof (*LocalPtr.BootMenuItem) - sizeof (LocalPtr.BootMenuItem->Data);
+
+ MenuLth -= MenuItemLen;
+ LocalPtr.BytePtr += MenuItemLen;
+ } while (MenuLth > 0);
+
+ if (MenuLth != 0) {
+ //
+ // missing required field
+ //
+ return FALSE;
+ }
+ }
+
+ if (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) {
+ DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultBootItem;
+ }
+
+ return TRUE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+BOOLEAN
+DHCPAckEdit (
+ DHCP_RECEIVE_BUFFER *RxBufPtr
+ )
+{
+ return (BOOLEAN) (DHCPOfferAckEdit (RxBufPtr) ? AckEdit (RxBufPtr) : FALSE);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// get an offer/ack
+//
+EFI_STATUS
+GetOfferAck (
+ PXE_BASECODE_DEVICE *Private,
+ BOOLEAN (*ExtraEdit)(DHCP_RECEIVE_BUFFER *DhcpRxBuf),
+ UINT16 OpFlags, // for Udp read
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
+ EFI_IP_ADDRESS *ClientIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ClientPortPtr,
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf,
+ EFI_EVENT TimeoutEvent
+ )
+/*++
+Routine description:
+ Wait for an OFFER/ACK packet.
+
+Parameters:
+ Private := Pointer to PxeBc interface
+ ExtraEdit := Pointer to extra option checking function
+ OpFlags := UdpRead() option flags
+ ServerIpPtr :=
+ ServerPortPtr :=
+ ClientIpPtr :=
+ ClientPortPtr :=
+ DhcpRxBuf :=
+ TimeoutEvent :=
+
+Returns:
+--*/
+{
+ EFI_IP_ADDRESS ServerIp;
+ EFI_STATUS StatCode;
+ INTN RxBufLen;
+
+ for (;;) {
+ //
+ // Wait until we get a UDP packet.
+ //
+ ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS));
+ RxBufLen = sizeof RxBuf[0].u.ReceiveBuffer;
+
+ if ((StatCode = UdpRead (
+ Private,
+ OpFlags,
+ ClientIpPtr,
+ ClientPortPtr,
+ ServerIpPtr,
+ ServerPortPtr,
+ 0,
+ 0,
+ (UINTN *) &RxBufLen,
+ &DhcpRxBuf->u.Dhcpv4,
+ TimeoutEvent
+ )) != EFI_SUCCESS) {
+ if (StatCode == EFI_TIMEOUT) {
+ StatCode = EFI_NO_RESPONSE;
+ }
+
+ break;
+ }
+ //
+ // got a packet - see if a good offer
+ //
+ if (DhcpRxBuf->u.Dhcpv4.op != BOOTP_REPLY) {
+ continue;
+ }
+
+ if (DhcpRxBuf->u.Dhcpv4.xid != DHCPV4_TRANSMIT_BUFFER.xid) {
+ continue;
+ }
+
+ if (*(UINT32 *) DHCPV4_TRANSMIT_BUFFER.options != * (UINT32 *) DhcpRxBuf->u.Dhcpv4.options) {
+ continue;
+ }
+
+ if (*(UINT8 *) &DhcpRxBuf->u.Dhcpv4.yiaddr > 223) {
+ continue;
+ }
+
+ if (CompareMem (
+ DhcpRxBuf->u.Dhcpv4.chaddr,
+ DHCPV4_TRANSMIT_BUFFER.chaddr,
+ sizeof DhcpRxBuf->u.Dhcpv4.chaddr
+ )) {
+ //
+ // no good
+ //
+ continue;
+ }
+
+ Parse (DhcpRxBuf, RxBufLen);
+
+ if (!(*ExtraEdit) (DhcpRxBuf)) {
+ continue;
+ }
+ //
+ // Good DHCP packet.
+ //
+ StatCode = EFI_SUCCESS;
+ break;
+ }
+
+ return StatCode;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// get DHCPOFFER's
+//
+EFI_STATUS
+GetOffers (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ EFI_IP_ADDRESS ClientIp;
+ EFI_IP_ADDRESS ServerIp;
+ EFI_STATUS StatCode;
+ EFI_EVENT TimeoutEvent;
+ INTN NumOffers;
+ INTN Index;
+
+ //
+ //
+ //
+ ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS));
+ NumOffers = 0;
+
+ for (Index = 0; Index < (sizeof Private->ServerCount) / sizeof Private->ServerCount[0]; ++Index) {
+ Private->ServerCount[Index] = 0;
+ Private->GotProxy[Index] = 0;
+ }
+
+ Private->GotBootp = 0;
+ //
+ // these we throw away
+ //
+ Private->GotProxy[DHCP_ONLY_IX] = 1;
+ StatCode = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ return StatCode;
+ }
+
+ StatCode = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ Private->Timeout * 10000000 + 1000000
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return StatCode;
+ }
+ //
+ // get offers
+ //
+ for (;;) {
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf;
+ UNION_PTR LocalPtr;
+
+ DhcpRxBuf = &RxBuf[NumOffers];
+
+ if ((
+ StatCode = GetOfferAck (
+ Private,
+ DHCPOfferAckEdit,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP |
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
+ &ServerIp,
+ &DhcpServerPort,
+ &ClientIp,
+ &DHCPClientPort,
+ DhcpRxBuf,
+ TimeoutEvent
+ )
+) != EFI_SUCCESS
+ ) {
+ break;
+ }
+
+ LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1];
+
+ //
+ // check type of offer
+ //
+ if (LocalPtr.OpPtr == NULL) {
+ //
+ // bootp - we only need one and make sure has bootfile
+ //
+ if (Private->GotBootp || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
+ continue;
+ }
+
+ Private->GotBootp = (UINT8) (NumOffers + 1);
+ }
+ //
+ // if a DHCP type, must be DHCPOFFER and must have server id
+ //
+ else if (LocalPtr.MessageType->Type != DHCPOFFER || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]) {
+ continue;
+ } else {
+ INTN TypeIx;
+
+ //
+ // get type - PXE10, WfM11a, or BINL
+ //
+ if (DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) {
+ TypeIx = PXE10_IX;
+ } else if (DhcpRxBuf->OpAdds.Status & WfM11a_TYPE) {
+ //
+ // WfM - make sure it has a bootfile
+ //
+ if (!DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
+ continue;
+ }
+
+ TypeIx = WfM11a_IX;
+ } else {
+ TypeIx = (DhcpRxBuf->OpAdds.Status & PXE_TYPE) ? BINL_IX : DHCP_ONLY_IX;
+ }
+ //
+ // check DHCP or proxy
+ //
+ if (DhcpRxBuf->u.Dhcpv4.yiaddr == 0) {
+ //
+ // proxy - only need one of each type if not BINL
+ // and must have at least PXE_TYPE
+ //
+ if (TypeIx == BINL_IX) {
+ Private->BinlProxies[Private->GotProxy[BINL_IX]++] = (UINT8) NumOffers;
+ } else if (Private->GotProxy[TypeIx]) {
+ continue;
+ } else {
+ Private->GotProxy[TypeIx] = (UINT8) (NumOffers + 1);
+ }
+ } else {
+ Private->OfferCount[TypeIx][Private->ServerCount[TypeIx]++] = (UINT8) NumOffers;
+ }
+ }
+
+ if (++NumOffers == MAX_OFFERS) {
+ break;
+ }
+ }
+
+ gBS->CloseEvent (TimeoutEvent);
+ Private->NumOffersReceived = NumOffers;
+
+ return (Private->NumOffersReceived) ? EFI_SUCCESS : EFI_NO_RESPONSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// send DHCPDECLINE
+//
+STATIC
+VOID
+DeclineOffer (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ UINT16 SaveSecs;
+
+ PxebcMode = Private->EfiBc.Mode;
+ SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs;
+
+ DHCPV4_TRANSMIT_BUFFER.secs = 0;
+ DHCPV4_TRANSMIT_BUFFER.flags = 0;
+ SetMem (
+ DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opdeclinestr),
+ sizeof (DHCPOpStart) - sizeof (struct opdeclinestr),
+ OP_PAD
+ );
+ DHCPDECLINEoptions.DhcpMessageType.Type = DHCPDECLINE;
+ CopyMem (&DHCPDECLINEoptions.OpDeclineEnd, &DHCP_REQ_OPTIONS, sizeof (struct requestopendstr));
+
+ {
+ EFI_IP_ADDRESS TmpIp;
+
+ CopyMem (&TmpIp, &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, sizeof TmpIp);
+
+ DoUdpWrite (
+ Private,
+ &TmpIp,
+ &DhcpServerPort,
+ &PxebcMode->StationIp,
+ &DHCPClientPort
+ );
+ }
+
+ InitDhcpv4TxBuf (Private);
+ DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;
+ Private->GoodStationIp = FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// send DHCPRELEASE
+//
+STATIC
+BOOLEAN
+Release (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ UINT16 SaveSecs;
+
+ PxebcMode = Private->EfiBc.Mode;
+ SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs;
+ DHCPV4_TRANSMIT_BUFFER.secs = 0;
+
+ SetMem (
+ DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opreleasestr),
+ sizeof (DHCPOpStart) - sizeof (struct opreleasestr),
+ OP_PAD
+ );
+
+ DHCPRELEASEoptions.DhcpMessageType.Type = DHCPRELEASE;
+
+ CopyMem (
+ &DHCPRELEASEoptions.DhcServerIpPtr,
+ &(DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1],
+ sizeof DHCPRELEASEoptions.DhcServerIpPtr
+ );
+
+ DHCPRELEASEoptions.End[0] = OP_END;
+
+ {
+ EFI_IP_ADDRESS TmpIp;
+
+ CopyMem (&TmpIp, &DHCPRELEASEoptions.DhcServerIpPtr.Ip, sizeof TmpIp);
+
+ DoUdpWrite (
+ Private,
+ &TmpIp,
+ &DhcpServerPort,
+ &PxebcMode->StationIp,
+ &DHCPClientPort
+ );
+ }
+
+ InitDhcpv4TxBuf (Private);
+
+ DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;
+ Private->GoodStationIp = FALSE;
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+BOOLEAN
+GetBINLAck (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *ServerIpPtr
+ )
+{
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf;
+ EFI_STATUS StatCode;
+ EFI_EVENT TimeoutEvent;
+
+ //
+ //
+ //
+ StatCode = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ return FALSE;
+ }
+
+ StatCode = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ Private->Timeout * 10000000 + 1000000
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return FALSE;
+ }
+ //
+ //
+ //
+ DhcpRxBuf = &PXE_BINL_BUFFER;
+
+ for (;;) {
+ EFI_PXE_BASE_CODE_UDP_PORT BINLSrvPort;
+
+ BINLSrvPort = 0;
+
+ if (GetOfferAck (
+ Private,
+ AckEdit,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
+ ServerIpPtr,
+ &BINLSrvPort,
+ &Private->EfiBc.Mode->StationIp,
+ &PSEUDO_DHCP_CLIENT_PORT,
+ DhcpRxBuf,
+ TimeoutEvent
+ ) != EFI_SUCCESS) {
+ break;
+ }
+ //
+ // make sure from whom we wanted
+ //
+ if (!DhcpRxBuf->u.Dhcpv4.yiaddr && !CompareMem (
+ &ServerIpPtr->v4,
+ &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,
+ sizeof (ServerIpPtr->v4)
+ )) {
+ gBS->CloseEvent (TimeoutEvent);
+ //
+ // got an ACK from server
+ //
+ return TRUE;
+ }
+ }
+
+ gBS->CloseEvent (TimeoutEvent);
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// make sure we can get BINL
+// send DHCPREQUEST to PXE server
+//
+STATIC
+BOOLEAN
+TryBINL (
+ PXE_BASECODE_DEVICE *Private,
+ INTN OfferIx
+ )
+{
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf;
+ EFI_IP_ADDRESS ServerIp;
+ UINT16 SaveSecs;
+ INTN Index;
+
+ DhcpRxBuf = &RxBuf[OfferIx];
+
+ //
+ // use next server address first.
+ //
+ ServerIp.Addr[0] = DhcpRxBuf->u.Dhcpv4.siaddr;
+ if (ServerIp.Addr[0] == 0) {
+ //
+ // next server address is NULL, use option 54.
+ //
+ CopyMem (
+ ((EFI_IPv4_ADDRESS *) &ServerIp),
+ &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ //
+ // client IP address - filled in by client if it knows it
+ //
+ CopyMem (
+ ((EFI_IPv4_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr),
+ &DHCP_REQ_OPTIONS.OpReqIP.Ip,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ SetMem (&DHCP_REQ_OPTIONS, sizeof DHCP_REQ_OPTIONS, OP_PAD);
+ DHCPV4_TRANSMIT_BUFFER.flags = 0;
+ DHCPV4_OPTIONS_BUFFER.End[0] = OP_END;
+ AddRouters (Private, DhcpRxBuf);
+ SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs;
+
+ for (Index = 0; Index < 3; Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Index) {
+ DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);
+
+ //
+ // unicast DHCPREQUEST to PXE server
+ //
+ if (DoUdpWrite (
+ Private,
+ &ServerIp,
+ &PseudoDhcpServerPort,
+ (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
+ &PSEUDO_DHCP_CLIENT_PORT
+ ) != EFI_SUCCESS) {
+ break;
+ }
+
+ if (!GetBINLAck (Private, &ServerIp)) {
+ continue;
+ }
+ //
+ // early exit failures
+ // make sure a good ACK
+ //
+ if (!DHCPOfferAckEdit (&PXE_BINL_BUFFER) || (
+ !(PXE_BINL_BUFFER.OpAdds.Status & DISCOVER_TYPE) && !PXE_BINL_BUFFER.OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]
+ )
+ ) {
+ break;
+ }
+
+ Private->EfiBc.Mode->ProxyOfferReceived = TRUE;
+ return TRUE;
+ }
+ //
+ // failed - reset seconds field, etc.
+ //
+ Private->EfiBc.Mode->RouteTableEntries = 0;
+ //
+ // reset
+ //
+ DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+BOOLEAN
+TryFinishBINL (
+ PXE_BASECODE_DEVICE *Private,
+ INTN OfferIx
+ )
+{
+ if (TryBINL (Private, OfferIx)) {
+ return TRUE;
+ }
+
+ return Release (Private);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+BOOLEAN
+TryFinishProxyBINL (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ INTN Index;
+
+ for (Index = 0; Index < Private->GotProxy[BINL_IX]; ++Index) {
+ if (TryBINL (Private, Private->BinlProxies[Index])) {
+ return TRUE;
+ }
+ }
+
+ return Release (Private);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// try to finish DORA - send DHCP request, wait for ACK, check with ARP
+//
+STATIC
+BOOLEAN
+TryFinishDORA (
+ PXE_BASECODE_DEVICE *Private,
+ INTN OfferIx
+ )
+{
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf;
+ EFI_IP_ADDRESS ClientIp;
+ EFI_IP_ADDRESS ServerIp;
+ EFI_STATUS StatCode;
+ UNION_PTR LocalPtr;
+ EFI_EVENT TimeoutEvent;
+
+ //
+ // send DHCP request
+ // if fail return false
+ //
+ DhcpRxBuf = &DHCPV4_ACK_BUFFER;
+ DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type = DHCPREQUEST;
+ CopyMem (&DHCP_REQ_OPTIONS, &RequestOpEndStr, sizeof (RequestOpEndStr));
+// DHCP_REQ_OPTIONS = RequestOpEndStr;
+ DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[OfferIx].u.Dhcpv4.yiaddr;
+
+ CopyMem (
+ &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip,
+ &((DHCPV4_OP_SERVER_IP *) RxBuf[OfferIx].OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,
+ sizeof DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip
+ );
+
+ CopyMem (
+ Private->EfiBc.Mode->SubnetMask.Addr,
+ &DefaultSubnetMask,
+ 4
+ );
+
+ //
+ // broadcast DHCPREQUEST
+ //
+ if (DoUdpWrite (
+ Private,
+ &BroadcastIP,
+ &DhcpServerPort,
+ (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
+ &DHCPClientPort
+ ) != EFI_SUCCESS) {
+ return FALSE;
+ }
+ //
+ //
+ //
+ StatCode = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ return FALSE;
+ }
+
+ StatCode = gBS->SetTimer (
+ TimeoutEvent,
+ TimerPeriodic,
+ Private->Timeout * 10000000 + 1000000
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return FALSE;
+ }
+ //
+ // wait for ACK
+ //
+ for (;;) {
+ if (GetOfferAck (
+ Private,
+ DHCPAckEdit,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP,
+ &ServerIp,
+ &DhcpServerPort,
+ &ClientIp,
+ &DHCPClientPort,
+ DhcpRxBuf,
+ TimeoutEvent
+ ) != EFI_SUCCESS) {
+ break;
+ }
+ //
+ // check type of response - need DHCPACK
+ //
+ if (CompareMem (
+ &DHCP_REQ_OPTIONS.OpReqIP.Ip,
+ &DhcpRxBuf->u.Dhcpv4.yiaddr,
+ sizeof (EFI_IPv4_ADDRESS)
+ ) || CompareMem (
+ &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip,
+ &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,
+ sizeof (EFI_IPv4_ADDRESS)
+ )) {
+ continue;
+ }
+ //
+ // got ACK
+ // check with ARP that IP unused - good return true
+ //
+ if (!SetStationIP (Private)) {
+ //
+ // fail - send DHCPDECLINE and return false
+ //
+ DeclineOffer (Private);
+ break;
+ }
+
+ LocalPtr.OpPtr = DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1];
+
+ if (LocalPtr.OpPtr != NULL) {
+ CopyMem (
+ (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask,
+ &LocalPtr.SubnetMaskStr->Ip,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ AddRouters (Private, DhcpRxBuf);
+ gBS->CloseEvent (TimeoutEvent);
+ return TRUE;
+ }
+
+ gBS->CloseEvent (TimeoutEvent);
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// try a DHCP server of appropriate type
+//
+STATIC
+BOOLEAN
+TryDHCPFinishDORA (
+ PXE_BASECODE_DEVICE *Private,
+ INTN TypeIx
+ )
+{
+ INTN Index;
+
+ //
+ // go through the DHCP servers of the requested type
+ //
+ for (Index = 0; Index < Private->ServerCount[TypeIx]; ++Index) {
+ if (TryFinishDORA (Private, Index = Private->OfferCount[TypeIx][Index])) {
+ if (TypeIx == BINL_IX && !TryFinishBINL (Private, Index)) {
+ continue;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// try a DHCP only server and a proxy of appropriate type
+//
+STATIC
+BOOLEAN
+TryProxyFinishDORA (
+ PXE_BASECODE_DEVICE *Private,
+ INTN TypeIx
+ )
+{
+ INTN Index;
+
+ if (!Private->GotProxy[TypeIx]) {
+ //
+ // no proxies of the type wanted
+ //
+ return FALSE;
+ }
+ //
+ // go through the DHCP only servers
+ //
+ for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) {
+ if (TryFinishDORA (Private, Private->OfferCount[DHCP_ONLY_IX][Index])) {
+ if (TypeIx != BINL_IX) {
+ CopyProxyRxBuf (Private, Private->GotProxy[TypeIx] - 1);
+ } else if (!TryFinishProxyBINL (Private)) {
+ //
+ // if didn't work with this DHCP, won't work with any
+ //
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// getting to the bottom of the barrel
+//
+STATIC
+BOOLEAN
+TryAnyWithBootfileFinishDORA (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ //
+ // try a DHCP only server who has a bootfile
+ //
+ UNION_PTR LocalPtr;
+ INTN Index;
+
+ for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) {
+ INTN offer;
+
+ offer = Private->OfferCount[DHCP_ONLY_IX][Index];
+
+ if (RxBuf[offer].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && TryFinishDORA (Private, offer)) {
+ return TRUE;
+ }
+ }
+ //
+ // really at bottom - see if be have any bootps
+ //
+ if (!Private->GotBootp) {
+ return FALSE;
+ }
+
+ DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[Private->GotBootp - 1].u.Dhcpv4.yiaddr;
+
+ if (!SetStationIP (Private)) {
+ return FALSE;
+ }
+ //
+ // treat BOOTP response as DHCP ACK packet
+ //
+ CopyParseRxBuf (Private, Private->GotBootp - 1, DHCPV4_ACK_INDEX);
+
+ LocalPtr.OpPtr = RxBuf[Private->GotBootp - 1].OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1];
+
+ if (LocalPtr.OpPtr != NULL) {
+ *(EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask = LocalPtr.SubnetMaskStr->Ip;
+ }
+
+ return TRUE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* DoDhcpDora()
+ */
+STATIC
+EFI_STATUS
+DoDhcpDora (
+ PXE_BASECODE_DEVICE *Private,
+ BOOLEAN SortOffers
+ )
+{
+ EFI_PXE_BASE_CODE_IP_FILTER Filter;
+ EFI_STATUS StatCode;
+ INTN NumOffers;
+
+ Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP | EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;
+
+ Filter.IpCnt = 0;
+ Filter.reserved = 0;
+
+ //
+ // set filter unicast or broadcast
+ //
+ if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {
+ return StatCode;
+ }
+ //
+ // seed random number with hardware address
+ //
+ SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress);
+
+ for (Private->Timeout = 1;
+ Private->Timeout < 17;
+ Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), Private->Timeout <<= 1
+ ) {
+ INTN Index;
+
+ InitDhcpv4TxBuf (Private);
+ DHCPV4_TRANSMIT_BUFFER.xid = Random (Private);
+ DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);
+
+ //
+ // broadcast DHCPDISCOVER
+ //
+ StatCode = DoUdpWrite (
+ Private,
+ &BroadcastIP,
+ &DhcpServerPort,
+ (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
+ &DHCPClientPort
+ );
+
+ if (StatCode != EFI_SUCCESS) {
+ return StatCode;
+ }
+
+ CopyMem (
+ &Private->EfiBc.Mode->DhcpDiscover,
+ (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER,
+ sizeof (EFI_PXE_BASE_CODE_PACKET)
+ );
+
+ //
+ // get DHCPOFFER's
+ //
+ if ((StatCode = GetOffers (Private)) != EFI_SUCCESS) {
+ if (StatCode != EFI_NO_RESPONSE) {
+ return StatCode;
+ }
+
+ continue;
+ }
+ //
+ // select offer and reply DHCPREQUEST
+ //
+ if (SortOffers) {
+ if (TryDHCPFinishDORA(Private, PXE10_IX) || // try DHCP with PXE10
+ TryDHCPFinishDORA(Private, WfM11a_IX) || // no - try with WfM
+ TryProxyFinishDORA(Private, PXE10_IX) || // no - try DHCP only and proxy with PXE10
+ TryProxyFinishDORA(Private, WfM11a_IX) || // no - try DHCP only and proxy with WfM
+ TryDHCPFinishDORA(Private, BINL_IX) || // no - try with WfM
+ TryProxyFinishDORA(Private, BINL_IX) || // no - try DHCP only and proxy with PXE10
+ TryAnyWithBootfileFinishDORA(Private))
+ {
+ return EFI_SUCCESS;
+ }
+
+ continue;
+ }
+ //
+ // FIFO order
+ //
+ NumOffers = Private->NumOffersReceived;
+
+ for (Index = 0; Index < NumOffers; ++Index) {
+ //
+ // ignore proxies
+ //
+ if (!RxBuf[Index].u.Dhcpv4.yiaddr) {
+ continue;
+ }
+ //
+ // check if a bootp server
+ //
+ if (!RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]) {
+ //
+ // it is - just check ARP
+ //
+ if (!SetStationIP (Private)) {
+ continue;
+ }
+ }
+ //
+ // else check if a DHCP only server
+ //
+ else if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE))) {
+ //
+ // it is a normal DHCP offer (without any PXE options), just finish the D.O.R.A by sending DHCP request.
+ //
+ if (!TryFinishDORA (Private, Index)) {
+ continue;
+ }
+ } else if (TryFinishDORA (Private, Index)) {
+ if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) && !TryFinishBINL (Private, Index)) {
+ continue;
+ }
+ }
+
+ DEBUG ((DEBUG_WARN, "\nDoDhcpDora() Got packets. "));
+ return EFI_SUCCESS;
+ }
+ //
+ // now look for DHCP onlys and a Proxy
+ //
+ for (Index = 0; Index < NumOffers; ++Index) {
+ INT8 Index2;
+
+ //
+ // ignore proxies, bootps, non DHCP onlys, and bootable DHCPS
+ //
+ if (!RxBuf[Index].u.Dhcpv4.yiaddr ||
+ !RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1] ||
+ RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE) ||
+ RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]
+ ) {
+ continue;
+ }
+ //
+ // found non bootable DHCP only - try to find a proxy
+ //
+ for (Index2 = 0; Index2 < NumOffers; ++Index2) {
+ if (!RxBuf[Index2].u.Dhcpv4.yiaddr) {
+ if (!TryFinishDORA (Private, Index)) {
+ //
+ // DHCP no ACK
+ //
+ break;
+ }
+
+ if (RxBuf[Index2].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) {
+ CopyProxyRxBuf (Private, Index2);
+ } else if (!TryFinishBINL (Private, Index2)) {
+ continue;
+ }
+
+ DEBUG ((DEBUG_WARN, "\nDoDhcpDora() Got packets. "));
+ return EFI_SUCCESS;
+ }
+ }
+ }
+ }
+
+ return EFI_NO_RESPONSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// determine if the server ip is in the ip list
+//
+BOOLEAN
+InServerList (
+ EFI_IP_ADDRESS *ServerIpPtr,
+ PXE_SERVER_LISTS *ServerListPtr
+ )
+{
+ UINTN Index;
+
+ if (!ServerListPtr || !ServerListPtr->Ipv4List.IpCount) {
+ return TRUE;
+ }
+
+ for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) {
+ if (!CompareMem (
+ ServerIpPtr,
+ &ServerListPtr->Ipv4List.IpList[Index],
+ sizeof (EFI_IPv4_ADDRESS)
+ )) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+BOOLEAN
+ExtractBootServerList (
+ UINT16 Type,
+ DHCPV4_OP_STRUCT *ptr,
+ PXE_SERVER_LISTS **ServerListPtr
+ )
+{
+ UNION_PTR LocalPtr;
+ INTN ServerListLen;
+
+ LocalPtr.OpPtr = ptr;
+ ServerListLen = LocalPtr.BootServersStr->Header.Length;
+
+ //
+ // find type
+ //
+ LocalPtr.BootServerList = LocalPtr.BootServersStr->ServerList;
+
+ while (ServerListLen) {
+ INTN ServerEntryLen;
+
+ ServerEntryLen = sizeof (PXEV4_SERVER_LIST) + 2 + (LocalPtr.BootServerList->u.Ipv4List.IpCount - 1) *
+ sizeof (EFI_IPv4_ADDRESS);
+
+ if (NTOHS (LocalPtr.BootServerList->Type) == Type) {
+ *ServerListPtr = &LocalPtr.BootServerList->u;
+ return TRUE;
+ }
+
+ (LocalPtr.BytePtr) += ServerEntryLen;
+ ServerListLen -= ServerEntryLen;
+ }
+
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+VOID
+FreeMem (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ if (Private->TransmitBuffer != NULL) {
+ gBS->FreePool (Private->TransmitBuffer);
+ Private->TransmitBuffer = NULL;
+ }
+
+ if (Private->ReceiveBuffers != NULL) {
+ gBS->FreePool (Private->ReceiveBuffers);
+ Private->ReceiveBuffers = NULL;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+BOOLEAN
+GetMem (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ EFI_STATUS Status;
+
+ if (Private->DhcpPacketBuffer == NULL) {
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1),
+ &Private->DhcpPacketBuffer
+ );
+
+ if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) {
+ Private->DhcpPacketBuffer = NULL;
+ FreeMem (Private);
+ return FALSE;
+ }
+ }
+
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ sizeof (EFI_PXE_BASE_CODE_PACKET),
+ &Private->TransmitBuffer
+ );
+
+ if (EFI_ERROR (Status) || Private->TransmitBuffer == NULL) {
+ gBS->FreePool (Private->DhcpPacketBuffer);
+ Private->DhcpPacketBuffer = NULL;
+ Private->TransmitBuffer = NULL;
+ FreeMem (Private);
+ return FALSE;
+ }
+
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ sizeof (DHCP_RECEIVE_BUFFER) * (MAX_OFFERS),
+ &Private->ReceiveBuffers
+ );
+
+ if (EFI_ERROR (Status) || Private->ReceiveBuffers == NULL) {
+ gBS->FreePool (Private->TransmitBuffer);
+ gBS->FreePool (Private->DhcpPacketBuffer);
+ Private->DhcpPacketBuffer = NULL;
+ Private->TransmitBuffer = NULL;
+ Private->ReceiveBuffers = NULL;
+ FreeMem (Private);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+EFI_STATUS
+EFIAPI
+BcDhcp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN SortOffers
+ )
+{
+ EFI_PXE_BASE_CODE_IP_FILTER Filter;
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ PXE_BASECODE_DEVICE *Private;
+ EFI_STATUS StatCode;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+
+ Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ Filter.IpCnt = 0;
+ Filter.reserved = 0;
+
+ DEBUG ((DEBUG_INFO, "\nBcDhcp() Enter. "));
+
+ PxebcMode = Private->EfiBc.Mode;
+
+ if (!GetMem (Private)) {
+ DEBUG ((DEBUG_ERROR, "\nBcDhcp() GetMem() failed.\n"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ PxebcMode->DhcpDiscoverValid = FALSE;
+ PxebcMode->DhcpAckReceived = FALSE;
+ PxebcMode->ProxyOfferReceived = FALSE;
+
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP;
+
+ //
+ // Issue BC command
+ //
+ if (Private->TotalSeconds == 0) {
+ //
+ // put in seconds field of DHCP send packets
+ //
+ Private->TotalSeconds = 4;
+ }
+
+ if ((StatCode = DoDhcpDora (Private, SortOffers)) == EFI_SUCCESS) {
+ //
+ // success - copy packets
+ //
+ PxebcMode->DhcpDiscoverValid = PxebcMode->DhcpAckReceived = TRUE;
+
+ CopyMem (
+ &PxebcMode->DhcpAck,
+ (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_ACK_PACKET,
+ sizeof (EFI_PXE_BASE_CODE_PACKET)
+ );
+
+ if (PxebcMode->ProxyOfferReceived) {
+ CopyMem (
+ &PxebcMode->ProxyOffer,
+ (EFI_PXE_BASE_CODE_PACKET *) &PXE_OFFER_PACKET,
+ sizeof (EFI_PXE_BASE_CODE_PACKET)
+ );
+ }
+ }
+ //
+ // set filter back to unicast
+ //
+ IpFilter (Private, &Filter);
+
+ FreeMem (Private);
+
+ //
+ // Unlock the instance data
+ //
+ DEBUG ((DEBUG_WARN, "\nBcDhcp() Exit = %xh ", StatCode));
+
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+BOOLEAN
+VerifyCredentialOption (
+ UINT8 *tx,
+ UINT8 *rx
+ )
+{
+ UINTN n;
+
+ //
+ // Fail verification if either pointer is NULL.
+ //
+ if (tx == NULL || rx == NULL) {
+ return FALSE;
+ }
+ //
+ // Fail verification if tx[0] is not a credential type option
+ // or if the length is zero or not a multiple of four.
+ //
+ if (tx[0] != VEND_PXE_CREDENTIAL_TYPES || tx[1] == 0 || tx[1] % 4 != 0) {
+ return FALSE;
+ }
+ //
+ // Fail verification if rx[0] is not a credential type option
+ // or if the length is not equal to four.
+ //
+ if (rx[0] != VEND_PXE_CREDENTIAL_TYPES || rx[1] != 4) {
+ return FALSE;
+ }
+ //
+ // Look through transmitted credential types for a copy
+ // of the received credential type.
+ //
+ for (n = 0; n < tx[1]; n += 4) {
+ if (!CompareMem (&tx[n + 2], &rx[2], 4)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+EFI_STATUS
+DoDiscover (
+ PXE_BASECODE_DEVICE *Private,
+ UINT16 OpFlags,
+ IN UINT16 Type,
+ IN UINT16 *LayerPtr,
+ IN BOOLEAN UseBis,
+ EFI_IP_ADDRESS *DestPtr,
+ PXE_SERVER_LISTS *ServerListPtr
+ )
+{
+ EFI_PXE_BASE_CODE_UDP_PORT ClientPort;
+ EFI_PXE_BASE_CODE_UDP_PORT ServerPort;
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ EFI_STATUS StatCode;
+ EFI_EVENT TimeoutEvent;
+ UINT8 OpLen;
+
+ PxebcMode = Private->EfiBc.Mode;
+
+ if (DestPtr->Addr[0] == 0) {
+ DEBUG ((DEBUG_WARN, "\nDoDiscover() !DestPtr->Addr[0]"));
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // seed random number with hardware address
+ //
+ SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress);
+
+ if (DestPtr->Addr[0] == BroadcastIP.Addr[0]) {
+ ClientPort = DHCPClientPort;
+ ServerPort = DhcpServerPort;
+ } else {
+ ClientPort = PSEUDO_DHCP_CLIENT_PORT;
+ ServerPort = PseudoDhcpServerPort;
+ }
+
+ if (UseBis) {
+ *LayerPtr |= PXE_BOOT_LAYER_CREDENTIAL_FLAG;
+ } else {
+ *LayerPtr &= PXE_BOOT_LAYER_MASK;
+ }
+
+ for (Private->Timeout = 1;
+ Private->Timeout < 5;
+ Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Private->Timeout
+ ) {
+ InitDhcpv4TxBuf (Private);
+ //
+ // initialize DHCP message structure
+ //
+ DHCPV4_TRANSMIT_BUFFER.xid = Random (Private);
+ DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);
+ CopyMem (
+ &DHCPV4_TRANSMIT_BUFFER.ciaddr,
+ &PxebcMode->StationIp,
+ sizeof DHCPV4_TRANSMIT_BUFFER.ciaddr
+ );
+
+ DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type = DHCPREQUEST;
+ DISCOVERoptions.Header.OpCode = OP_VENDOR_SPECIFIC;
+ DISCOVERoptions.BootItem.Header.OpCode = VEND_PXE_BOOT_ITEM;
+ DISCOVERoptions.BootItem.Header.Length = DHCPV4_OPTION_LENGTH (PXE_OP_BOOT_ITEM);
+ DISCOVERoptions.BootItem.Type = HTONS (Type);
+ DISCOVERoptions.BootItem.Layer = HTONS (*LayerPtr);
+
+ if (UseBis) {
+ EFI_BIS_PROTOCOL *BisPtr;
+ BIS_APPLICATION_HANDLE BisAppHandle;
+ EFI_BIS_DATA *BisDataSigInfo;
+ EFI_BIS_SIGNATURE_INFO *BisSigInfo;
+ UINTN Index;
+ UINTN Index2;
+
+ BisPtr = PxebcBisStart (
+ Private,
+ &BisAppHandle,
+ &BisDataSigInfo
+ );
+
+ if (BisPtr == NULL) {
+ //
+ // %%TBD - In order to get here, BIS must have
+ // been present when PXEBC.Start() was called.
+ // BIS had to be shutdown/removed/damaged
+ // before PXEBC.Discover() was called.
+ // Do we need to document a specific error
+ // for this case?
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Compute number of credential types.
+ //
+ Index2 = BisDataSigInfo->Length / sizeof (EFI_BIS_SIGNATURE_INFO);
+
+ DISCREDoptions.Header.OpCode = VEND_PXE_CREDENTIAL_TYPES;
+
+ DISCREDoptions.Header.Length = (UINT8) (Index2 * sizeof (PXE_CREDENTIAL));
+
+ OpLen = (UINT8) (DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS) + sizeof (DHCPV4_OP_HEADER) + DISCREDoptions.Header.Length);
+
+ BisSigInfo = (EFI_BIS_SIGNATURE_INFO *) BisDataSigInfo->Data;
+
+ for (Index = 0; Index < Index2; ++Index) {
+ UINT32 x;
+
+ CopyMem (&x, &BisSigInfo[Index], sizeof x);
+ x = HTONL (x);
+ CopyMem (&DISCREDoptions.Credentials[Index], &x, sizeof x);
+ }
+
+ PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo);
+ } else {
+ OpLen = DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS);
+ }
+
+ DISCOVERoptions.Header.Length = OpLen;
+
+ ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen - 1] = OP_END;
+ ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen] = OP_END;
+
+ StatCode = DoUdpWrite (
+ Private,
+ DestPtr,
+ &ServerPort,
+ (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
+ &ClientPort
+ );
+
+ if (StatCode != EFI_SUCCESS) {
+ return StatCode;
+ }
+ //
+ //
+ //
+ StatCode = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ return StatCode;
+ }
+
+ StatCode = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ Private->Timeout * 10000000 + 1000000
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return StatCode;
+ }
+ //
+ // wait for ACK
+ //
+ for (;;) {
+ DHCP_RECEIVE_BUFFER *RxBufPtr;
+ UINT16 TmpType;
+ UINT16 TmpLayer;
+
+ RxBufPtr = UseBis ? &PXE_BIS_BUFFER : &PXE_ACK_BUFFER;
+ ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));
+
+ if (GetOfferAck (
+ Private,
+ AckEdit,
+ OpFlags,
+ (EFI_IP_ADDRESS *) &Private->ServerIp,
+ 0,
+ (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,
+ &ClientPort,
+ RxBufPtr,
+ TimeoutEvent
+ ) != EFI_SUCCESS) {
+ break;
+ }
+ //
+ // check type of response - need PXEClient DHCPACK of proper type with bootfile
+ //
+ if (!(RxBufPtr->OpAdds.Status & PXE_TYPE) ||
+ (UseBis && (RxBufPtr->OpAdds.Status & USE_THREE_BYTE)) ||
+ !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] ||
+ !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1] ||
+ !InServerList((EFI_IP_ADDRESS *)&((DHCPV4_OP_SERVER_IP *)RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX-1])->Ip, ServerListPtr)) {
+
+ continue;
+ }
+
+ TmpType = TmpLayer = 0;
+
+ if (RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) {
+ TmpType = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Type);
+
+ if (RxBufPtr->OpAdds.Status & USE_THREE_BYTE) {
+ TmpLayer = (UINT16) (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer >> 8);
+ } else {
+ TmpLayer = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer);
+ }
+ }
+
+ if (TmpType != Type) {
+ continue;
+ }
+
+ if (UseBis) {
+ if (!RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1]) {
+ continue;
+ }
+
+ if (!VerifyCredentialOption (
+ (UINT8 *) &DISCREDoptions.Header,
+ (UINT8 *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1]
+ )) {
+ continue;
+ }
+ }
+
+ *LayerPtr = TmpLayer;
+
+ if (UseBis) {
+ CopyMem (
+ &PxebcMode->PxeBisReply,
+ &RxBufPtr->u.Dhcpv4,
+ sizeof (EFI_PXE_BASE_CODE_PACKET)
+ );
+
+ PxebcMode->PxeBisReplyReceived = TRUE;
+
+ StatCode = DoDiscover (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
+ Type,
+ LayerPtr,
+ FALSE,
+ &Private->ServerIp,
+ 0
+ );
+
+ gBS->CloseEvent (TimeoutEvent);
+ return StatCode;
+ }
+
+ PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = TRUE;
+
+ CopyMem (
+ &PxebcMode->PxeDiscover,
+ &*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER,
+ sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER)
+ );
+
+ CopyMem (
+ &PxebcMode->PxeReply,
+ &*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4,
+ sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4)
+ );
+
+ AddRouters (Private, RxBufPtr);
+
+ gBS->CloseEvent (TimeoutEvent);
+ return EFI_SUCCESS;
+ }
+
+ gBS->CloseEvent (TimeoutEvent);
+ }
+ //
+ // end for loop
+ //
+ return EFI_TIMEOUT;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+ Parameters:
+ Private := Pointer to PxeBc interface
+ Type :=
+ LayerPtr :=
+ UseBis :=
+ DiscoverInfoPtr :=
+ McastServerListPtr :=
+ ServerListPtr :=
+
+
+**/
+STATIC
+EFI_STATUS
+Discover (
+ PXE_BASECODE_DEVICE *Private,
+ IN UINT16 Type,
+ IN UINT16 *LayerPtr,
+ IN BOOLEAN UseBis,
+ IN EFI_PXE_BASE_CODE_DISCOVER_INFO *DiscoverInfoPtr,
+ PXE_SERVER_LISTS *McastServerListPtr,
+ PXE_SERVER_LISTS *ServerListPtr
+ )
+{
+ EFI_IP_ADDRESS DestIp;
+ EFI_STATUS StatCode;
+
+ DEBUG ((DEBUG_INFO, "\nDiscover() Type=%d Layer=%d ", Type, *LayerPtr));
+
+ if (UseBis) {
+ DEBUG ((DEBUG_INFO, "BIS "));
+ }
+ //
+ // get dest IP addr - mcast, bcast, or unicast
+ //
+ if (DiscoverInfoPtr->UseMCast) {
+ DestIp.v4 = DiscoverInfoPtr->ServerMCastIp.v4;
+
+ DEBUG (
+ (DEBUG_INFO,
+ "\nDiscover() MCast %d.%d.%d.%d ",
+ DestIp.v4.Addr[0],
+ DestIp.v4.Addr[1],
+ DestIp.v4.Addr[2],
+ DestIp.v4.Addr[3])
+ );
+
+ if ((StatCode = DoDiscover (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
+ Type,
+ LayerPtr,
+ UseBis,
+ &DestIp,
+ McastServerListPtr
+ )) != EFI_TIMEOUT) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nDiscover() status == %r (%Xh)",
+ StatCode,
+ StatCode)
+ );
+
+ return StatCode;
+ }
+ }
+
+ if (DiscoverInfoPtr->UseBCast) {
+ DEBUG ((DEBUG_INFO, "\nDiscver() BCast "));
+
+ if ((StatCode = DoDiscover (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
+ Type,
+ LayerPtr,
+ UseBis,
+ &BroadcastIP,
+ McastServerListPtr
+ )) != EFI_TIMEOUT) {
+
+ DEBUG ((DEBUG_WARN, "\nDiscover() status == %r (%Xh)", StatCode, StatCode));
+
+ return StatCode;
+ }
+ }
+
+ if (DiscoverInfoPtr->UseUCast) {
+ UINTN Index;
+
+ DEBUG (
+ (DEBUG_INFO,
+ "\nDiscover() UCast IP#=%d ",
+ ServerListPtr->Ipv4List.IpCount)
+ );
+
+ for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) {
+ CopyMem (&DestIp, &ServerListPtr->Ipv4List.IpList[Index], 4);
+
+ DEBUG (
+ (DEBUG_INFO,
+ "\nDiscover() UCast %d.%d.%d.%d ",
+ DestIp.v4.Addr[0],
+ DestIp.v4.Addr[1],
+ DestIp.v4.Addr[2],
+ DestIp.v4.Addr[3])
+ );
+
+ if ((StatCode = DoDiscover (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
+ Type,
+ LayerPtr,
+ UseBis,
+ &DestIp,
+ 0
+ )) != EFI_TIMEOUT) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nDiscover() status == %r (%Xh)",
+ StatCode,
+ StatCode)
+ );
+
+ return StatCode;
+ }
+ }
+ }
+
+ DEBUG ((DEBUG_WARN, "\nDiscover() TIMEOUT"));
+
+ return EFI_TIMEOUT;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* BcDiscover()
+ */
+
+/**
+
+
+**/
+EFI_STATUS
+EFIAPI
+BcDiscover (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN UINT16 Type,
+ IN UINT16 *LayerPtr,
+ IN BOOLEAN UseBis,
+ IN EFI_PXE_BASE_CODE_DISCOVER_INFO * DiscoverInfoPtr OPTIONAL
+ )
+{
+ EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ DHCP_RECEIVE_BUFFER *DhcpRxBuf;
+ PXE_SERVER_LISTS DefaultSrvList;
+ PXE_SERVER_LISTS *ServerListPtr;
+ PXE_SERVER_LISTS *McastServerListPtr;
+ UNION_PTR LocalPtr;
+ UINTN Index;
+ UINTN Index2;
+ BOOLEAN AcquiredSrvList;
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+
+ ServerListPtr = NULL;
+ McastServerListPtr = NULL;
+ AcquiredSrvList = FALSE;
+
+ PxebcMode = Private->EfiBc.Mode;
+
+ if (!GetMem (Private)) {
+ EfiReleaseLock (&Private->Lock);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (UseBis) {
+ if (!PxebcMode->BisSupported) {
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;
+
+ if (Private->TotalSeconds == 0) {
+ //
+ // put in seconds field of DHCP send packets
+ //
+ Private->TotalSeconds = 4;
+ }
+
+ ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO));
+
+ //
+ // if layer number not zero, use previous discover
+ //
+ if (*LayerPtr != 0) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0"));
+
+ if (DiscoverInfoPtr != NULL) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && DiscoverInfoPtr != NULL\n"));
+
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!PxebcMode->PxeDiscoverValid) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeDiscoverValid == 0\n"));
+
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!PxebcMode->PxeReplyReceived) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeReplyReceived == 0\n"));
+
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (UseBis && !PxebcMode->PxeBisReplyReceived) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeBisReplyReceived == 0\n"));
+
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DefaultInfo.UseUCast = TRUE;
+ DiscoverInfoPtr = &DefaultInfo;
+
+ DefaultSrvList.Ipv4List.IpCount = 1;
+ CopyMem (&DefaultSrvList.Ipv4List.IpList[0], &Private->ServerIp, 4);
+
+ ServerListPtr = &DefaultSrvList;
+ }
+ //
+ // layer is zero - see if info is supplied or if we need to use info from a cached offer
+ //
+ else if (!DiscoverInfoPtr) {
+ //
+ // not supplied - generate it
+ // make sure that there is cached, appropriate information
+ // if neither DhcpAck packet nor ProxyOffer packet has pxe info, fail
+ //
+ DhcpRxBuf = (PxebcMode->ProxyOfferReceived) ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER;
+
+ if (!PxebcMode->DhcpAckReceived || !(DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE)) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() !ack && !proxy"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DiscoverInfoPtr = &DefaultInfo;
+
+ LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];
+
+ //
+ // if multicast enabled, need multicast address
+ //
+ if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST)) {
+ DefaultInfo.UseMCast = TRUE;
+
+ CopyMem (
+ ((EFI_IPv4_ADDRESS *) &DefaultInfo.ServerMCastIp),
+ &((DHCPV4_OP_IP_ADDRESS *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Ip,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ DefaultInfo.UseBCast = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & DISABLE_BCAST) == 0);
+
+ DefaultInfo.MustUseList = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) != 0);
+
+ DefaultInfo.UseUCast = (BOOLEAN)
+ (
+ (DefaultInfo.MustUseList) ||
+ ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST))
+ );
+
+ if ((DefaultInfo.UseUCast | DefaultInfo.MustUseList) && !ExtractBootServerList (
+ Type,
+ DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1],
+ &ServerListPtr
+ )) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() type not in list"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // Info supplied - make SrvList if required
+ // if we use ucast discovery or must use list, there better be one
+ //
+ else if (DiscoverInfoPtr->UseUCast || DiscoverInfoPtr->MustUseList) {
+ //
+ // there better be a list
+ //
+ if (DiscoverInfoPtr->IpCnt == 0) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() no bootserver list"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // get its size
+ //
+ for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) {
+ if (DiscoverInfoPtr->SrvList[Index].Type == Type) {
+ if (DiscoverInfoPtr->SrvList[Index].AcceptAnyResponse) {
+ if (Index2 != 0) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() accept any?"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ } else {
+ Index2 = 1;
+ DefaultSrvList.Ipv4List.IpCount = 0;
+ ServerListPtr = &DefaultSrvList;
+ break;
+ }
+ } else {
+ ++Index2;
+ }
+ }
+ }
+
+ if (Index2 == 0) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() !Index2?"));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ServerListPtr == NULL) {
+ ServerListPtr = AllocatePool (
+ sizeof (PXEV4_SERVER_LIST) + (Index2 - 1) * sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ if (ServerListPtr == NULL) {
+ EfiReleaseLock (&Private->Lock);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // build an array of IP addresses from the server list
+ //
+ AcquiredSrvList = TRUE;
+ ServerListPtr->Ipv4List.IpCount = (UINT8) Index2;
+
+ for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) {
+ if (DiscoverInfoPtr->SrvList[Index].Type == Type) {
+ CopyMem (
+ &ServerListPtr->Ipv4List.IpList[Index2++],
+ &DiscoverInfoPtr->SrvList[Index].IpAddr.v4,
+ sizeof ServerListPtr->Ipv4List.IpList[0]
+ );
+ }
+ }
+ }
+ }
+
+ if (DiscoverInfoPtr->MustUseList) {
+ McastServerListPtr = ServerListPtr;
+ }
+
+ if (!(DiscoverInfoPtr->UseMCast || DiscoverInfoPtr->UseBCast || DiscoverInfoPtr->UseUCast)) {
+ DEBUG ((DEBUG_WARN, "\nBcDiscover() Nothing to use!\n"));
+
+ EfiReleaseLock (&Private->Lock);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = PxebcMode->PxeBisReplyReceived = FALSE;
+
+ StatCode = Discover (
+ Private,
+ Type,
+ LayerPtr,
+ UseBis,
+ DiscoverInfoPtr,
+ McastServerListPtr,
+ ServerListPtr
+ );
+
+ if (AcquiredSrvList) {
+ gBS->FreePool (ServerListPtr);
+ }
+
+ FreeMem (Private);
+
+ //
+ // Unlock the instance data
+ //
+ DEBUG (
+ (DEBUG_INFO,
+ "\nBcDiscover() status == %r (%Xh)\n",
+ StatCode,
+ StatCode)
+ );
+
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+EFI_STATUS
+EFIAPI
+BcSetPackets (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ BOOLEAN *NewDhcpDiscoverValid, OPTIONAL
+ BOOLEAN *NewDhcpAckReceived, OPTIONAL
+ BOOLEAN *NewProxyOfferReceived, OPTIONAL
+ BOOLEAN *NewPxeDiscoverValid, OPTIONAL
+ BOOLEAN *NewPxeReplyReceived, OPTIONAL
+ BOOLEAN *NewPxeBisReplyReceived, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewDhcpDiscover, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewDhcpAck, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewProxyOffer, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeDiscover, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeReply, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET * NewPxeBisReply OPTIONAL
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxebcMode;
+ EFI_STATUS Status;
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+
+ PxebcMode = Private->EfiBc.Mode;
+
+ if (Private->DhcpPacketBuffer == NULL) {
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1),
+ &Private->DhcpPacketBuffer
+ );
+
+ if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) {
+ Private->DhcpPacketBuffer = NULL;
+ EfiReleaseLock (&Private->Lock);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+ //
+ // Issue BC command
+ //
+ //
+ // reset
+ //
+ Private->FileSize = 0;
+ if (NewDhcpDiscoverValid != NULL) {
+ PxebcMode->DhcpDiscoverValid = *NewDhcpDiscoverValid;
+ }
+
+ if (NewDhcpAckReceived != NULL) {
+ PxebcMode->DhcpAckReceived = *NewDhcpAckReceived;
+ }
+
+ if (NewProxyOfferReceived != NULL) {
+ PxebcMode->ProxyOfferReceived = *NewProxyOfferReceived;
+ }
+
+ if (NewPxeDiscoverValid != NULL) {
+ PxebcMode->PxeDiscoverValid = *NewPxeDiscoverValid;
+ }
+
+ if (NewPxeReplyReceived != NULL) {
+ PxebcMode->PxeReplyReceived = *NewPxeReplyReceived;
+ }
+
+ if (NewPxeBisReplyReceived != NULL) {
+ PxebcMode->PxeBisReplyReceived = *NewPxeBisReplyReceived;
+ }
+
+ if (NewDhcpDiscover != NULL) {
+ CopyMem (
+ &PxebcMode->DhcpDiscover,
+ NewDhcpDiscover,
+ sizeof *NewDhcpDiscover
+ );
+ }
+
+ if (NewDhcpAck != NULL) {
+ CopyParse (Private, &PxebcMode->DhcpAck, NewDhcpAck, DHCPV4_ACK_INDEX);
+ }
+
+ if (NewProxyOffer != NULL) {
+ CopyParse (Private, &PxebcMode->ProxyOffer, NewProxyOffer, PXE_OFFER_INDEX);
+ }
+
+ if (NewPxeDiscover != NULL) {
+ CopyMem (
+ &PxebcMode->PxeDiscover,
+ NewPxeDiscover,
+ sizeof *NewPxeDiscover
+ );
+ }
+
+ if (NewPxeReply != NULL) {
+ CopyParse (Private, &PxebcMode->PxeReply, NewPxeReply, PXE_ACK_INDEX);
+ }
+
+ if (NewPxeBisReply != NULL) {
+ CopyParse (Private, &PxebcMode->PxeBisReply, NewPxeBisReply, PXE_BIS_INDEX);
+ }
+ //
+ // Unlock the instance data
+ //
+ EfiReleaseLock (&Private->Lock);
+ return EFI_SUCCESS;
+}
+
+/* eof - pxe_bc_dhcp.c */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c
new file mode 100644
index 0000000000..c5088dff32
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c
@@ -0,0 +1,421 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+**/
+
+
+#define RAND_MAX 0x10000
+
+#include "Bc.h"
+
+//
+// Definitions for internet group management protocol version 2 message
+// structure Per RFC 2236, November 1997
+//
+STATIC UINT8 RouterAlertOption[4] = { 0x80 | 20, 4, 0, 0 };
+STATIC IPV4_ADDR AllRoutersGroup = { { 224, 0, 0, 2 } };
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+VOID
+ClearGroupTimer (
+ PXE_BASECODE_DEVICE *Private,
+ UINTN TimerId
+ )
+{
+ if (Private == NULL) {
+ return ;
+ }
+
+ if (TimerId >= Private->MCastGroupCount) {
+ return ;
+ }
+
+ if (Private->IgmpGroupEvent[TimerId] == NULL) {
+ return ;
+ }
+
+ gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);
+ Private->IgmpGroupEvent[TimerId] = NULL;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+VOID
+SetGroupTimer (
+ PXE_BASECODE_DEVICE *Private,
+ UINTN TimerId,
+ UINTN MaxRespTime
+ )
+{
+ EFI_STATUS EfiStatus;
+
+ if (Private == NULL) {
+ return ;
+ }
+
+ if (TimerId >= Private->MCastGroupCount) {
+ return ;
+ }
+
+ if (Private->IgmpGroupEvent[TimerId] != NULL) {
+ gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);
+ }
+
+ EfiStatus = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Private->IgmpGroupEvent[TimerId]
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ Private->IgmpGroupEvent[TimerId] = NULL;
+ return ;
+ }
+
+ EfiStatus = gBS->SetTimer (
+ Private->IgmpGroupEvent[TimerId],
+ TimerRelative,
+ MaxRespTime * 1000000 + Random (Private) % RAND_MAX
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);
+ Private->IgmpGroupEvent[TimerId] = NULL;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+VOID
+SendIgmpMessage (
+ PXE_BASECODE_DEVICE *Private,
+ UINT8 Type,
+ INTN GroupId
+ )
+{
+ Private->IgmpMessage.Type = Type;
+ Private->IgmpMessage.MaxRespTime = 0;
+ Private->IgmpMessage.Checksum = 0;
+ Private->IgmpMessage.GroupAddress = Private->MCastGroup[GroupId];
+ Private->IgmpMessage.Checksum = IpChecksum (
+ (UINT16 *) &Private->IgmpMessage,
+ sizeof Private->IgmpMessage
+ );
+
+ Ipv4SendWOp (
+ Private,
+ 0,
+ (UINT8 *) &Private->IgmpMessage,
+ sizeof Private->IgmpMessage,
+ PROT_IGMP,
+ RouterAlertOption,
+ sizeof RouterAlertOption,
+ ((Type == IGMP_TYPE_LEAVE_GROUP) ? AllRoutersGroup.L : Private->IgmpMessage.GroupAddress),
+ EFI_PXE_BASE_CODE_FUNCTION_IGMP
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+VOID
+ReportIgmp (
+ PXE_BASECODE_DEVICE *Private,
+ INTN GroupId
+ )
+{
+ //
+ // if version 1 querier, send v1 report
+ //
+ UINT8 Type;
+
+ if (Private->Igmpv1TimeoutEvent != NULL) {
+ if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) {
+ gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
+ Private->Igmpv1TimeoutEvent = NULL;
+ Private->UseIgmpv1Reporting = TRUE;
+ }
+ }
+
+ Type = (UINT8) (Private->UseIgmpv1Reporting ? IGMP_TYPE_V1REPORT : IGMP_TYPE_REPORT);
+
+ SendIgmpMessage (Private, Type, GroupId);
+ ClearGroupTimer (Private, GroupId);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+VOID
+IgmpCheckTimers (
+ PXE_BASECODE_DEVICE *Private
+ )
+{
+ UINTN GroupId;
+
+ if (Private == NULL) {
+ return ;
+ }
+
+ for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
+ if (Private->IgmpGroupEvent[GroupId] == NULL) {
+ continue;
+ }
+
+ if (!EFI_ERROR (gBS->CheckEvent (Private->IgmpGroupEvent[GroupId]))) {
+ //
+ // send a report
+ //
+ ReportIgmp (Private, GroupId);
+ }
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return 0 := Group not found
+ @return other := Group ID#
+
+**/
+STATIC
+INTN
+FindMulticastGroup (
+ PXE_BASECODE_DEVICE *Private,
+ UINT32 GroupAddress
+ )
+{
+ UINTN GroupId;
+
+ for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
+ if (Private->MCastGroup[GroupId] == GroupAddress) {
+ return GroupId + 1;
+ }
+ }
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+VOID
+IgmpJoinGroup (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *GroupPtr
+ )
+{
+ UINT32 Grp;
+
+ Grp = *(UINT32 *) GroupPtr;
+
+ //
+ // see if we already have it or if we can't take anymore
+ //
+ if (FindMulticastGroup (Private, Grp) || Private->MCastGroupCount == MAX_MCAST_GROUPS) {
+ return ;
+ }
+ //
+ // add the group
+ //
+ Private->MCastGroup[Private->MCastGroupCount] = Grp;
+
+ ReportIgmp (Private, Private->MCastGroupCount);
+ //
+ // send a report
+ // so it will get sent again per RFC 2236
+ //
+ SetGroupTimer (
+ Private,
+ Private->MCastGroupCount++,
+ UNSOLICITED_REPORT_INTERVAL * 10
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+VOID
+IgmpLeaveGroup (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *GroupPtr
+ )
+{
+ UINT32 Grp;
+ UINTN GroupId;
+
+ Grp = *(UINT32 *) GroupPtr;
+
+ //
+ // if not in group, ignore
+ //
+ GroupId = FindMulticastGroup (Private, Grp);
+
+ if (GroupId == 0) {
+ return ;
+ }
+ //
+ // if not v1 querrier, send leave group IGMP message
+ //
+ if (Private->Igmpv1TimeoutEvent != NULL) {
+ if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) {
+ gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
+ Private->Igmpv1TimeoutEvent = NULL;
+ Private->UseIgmpv1Reporting = TRUE;
+ } else {
+ SendIgmpMessage (Private, IGMP_TYPE_LEAVE_GROUP, GroupId - 1);
+ }
+ }
+
+ while (GroupId < Private->MCastGroupCount) {
+ Private->MCastGroup[GroupId - 1] = Private->MCastGroup[GroupId];
+ Private->IgmpGroupEvent[GroupId - 1] = Private->IgmpGroupEvent[GroupId];
+ ++GroupId;
+ }
+
+ --Private->MCastGroupCount;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+VOID
+HandleIgmp (
+ PXE_BASECODE_DEVICE *Private,
+ IGMPV2_MESSAGE *IgmpMessagePtr,
+ UINTN IgmpLength
+ )
+{
+ EFI_STATUS EfiStatus;
+ UINTN GroupId;
+ INTN MaxRespTime;
+
+ if (Private == NULL) {
+ return ;
+ }
+
+ if (Private->MCastGroupCount == 0) {
+ //
+ // if we don't belong to any multicast groups, ignore
+ //
+ return ;
+ }
+ //
+ // verify checksum
+ //
+ if (IpChecksum ((UINT16 *) IgmpMessagePtr, IgmpLength)) {
+ //
+ // bad checksum - ignore packet
+ //
+ return ;
+ }
+
+ switch (IgmpMessagePtr->Type) {
+ case IGMP_TYPE_QUERY:
+ //
+ // if a version 1 querier, note the fact and set max resp time
+ //
+ MaxRespTime = IgmpMessagePtr->MaxRespTime;
+
+ if (MaxRespTime == 0) {
+ Private->UseIgmpv1Reporting = TRUE;
+
+ if (Private->Igmpv1TimeoutEvent != NULL) {
+ gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
+ }
+
+ EfiStatus = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Private->Igmpv1TimeoutEvent
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ Private->Igmpv1TimeoutEvent = NULL;
+ } else {
+ EfiStatus = gBS->SetTimer (
+ Private->Igmpv1TimeoutEvent,
+ TimerRelative,
+ (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000
+ );
+ }
+
+ MaxRespTime = IGMP_DEFAULT_MAX_RESPONSE_TIME * 10;
+ }
+ //
+ // if a general query (!GroupAddress), set all our group timers
+ //
+ if (!IgmpMessagePtr->GroupAddress) {
+ for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
+ SetGroupTimer (Private, GroupId, MaxRespTime);
+ }
+ } else {
+ //
+ // specific query - set only specific group
+ //
+ GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress);
+
+ if (GroupId != 0) {
+ SetGroupTimer (Private, GroupId - 1, MaxRespTime);
+ }
+ }
+
+ break;
+
+ //
+ // if we have a timer running for this group, clear it
+ //
+ case IGMP_TYPE_V1REPORT:
+ case IGMP_TYPE_REPORT:
+ GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress);
+
+ if (GroupId != 0) {
+ ClearGroupTimer (Private, GroupId - 1);
+ }
+
+ break;
+ }
+}
+
+/* EOF - pxe_bc_igmp.c */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c
new file mode 100644
index 0000000000..6330679a8f
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c
@@ -0,0 +1,846 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ pxe_bc_ip.c
+
+Abstract:
+
+
+**/
+
+#include "Bc.h"
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+ Check if two IP addresses are on the same subnet.
+
+ @param IpLength Length of IP address in bytes.
+ @param Ip1 IP address to check.
+ @param Ip2 IP address to check.
+ @param SubnetMask Subnet mask to check with.
+
+ @retval TRUE IP addresses are on the same subnet.
+ @retval FALSE IP addresses are on different subnets.
+
+**/
+BOOLEAN
+OnSameSubnet (
+ IN UINTN IpLength,
+ IN EFI_IP_ADDRESS *Ip1,
+ IN EFI_IP_ADDRESS *Ip2,
+ IN EFI_IP_ADDRESS *SubnetMask
+ )
+{
+ if (IpLength == 0 || Ip1 == NULL || Ip2 == NULL || SubnetMask == NULL) {
+ return FALSE;
+ }
+
+ while (IpLength-- != 0) {
+ if ((Ip1->v6.Addr[IpLength] ^ Ip2->v6.Addr[IpLength]) & SubnetMask->v6.Addr[IpLength]) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+ Add router to router table.
+
+ @param Private Pointer PxeBc instance data.
+ @param RouterIpPtr Pointer to router IP address.
+
+ @return Nothing
+
+**/
+VOID
+IpAddRouter (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN EFI_IP_ADDRESS *RouterIpPtr
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ UINTN Index;
+
+ if (Private == NULL || RouterIpPtr == NULL) {
+ return ;
+ }
+
+ PxeBcMode = Private->EfiBc.Mode;
+
+ //
+ // if we are filled up or this is not on the same subnet, forget it
+ //
+ if ((PxeBcMode->RouteTableEntries == PXE_ROUTER_TABLE_SIZE) ||
+ !OnSameSubnet(Private->IpLength, &PxeBcMode->StationIp, RouterIpPtr, &PxeBcMode->SubnetMask)) {
+ return ;
+ }
+ //
+ // make sure we don't already have it
+ //
+ for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {
+ if (!CompareMem (
+ &PxeBcMode->RouteTable[Index].GwAddr,
+ RouterIpPtr,
+ Private->IpLength
+ )) {
+ return ;
+ }
+ }
+ //
+ // keep it
+ //
+ ZeroMem (
+ &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries],
+ sizeof (EFI_PXE_BASE_CODE_ROUTE_ENTRY)
+ );
+
+ CopyMem (
+ &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries++].GwAddr,
+ RouterIpPtr,
+ Private->IpLength
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// return router ip to use for DestIp (0 if none)
+//
+STATIC
+EFI_IP_ADDRESS *
+GetRouterIp (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *DestIpPtr
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ UINTN Index;
+
+ if (Private == NULL || DestIpPtr == NULL) {
+ return NULL;
+ }
+
+ PxeBcMode = Private->EfiBc.Mode;
+
+ for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {
+ if (OnSameSubnet (
+ Private->IpLength,
+ &PxeBcMode->RouteTable[Index].IpAddr,
+ DestIpPtr,
+ &PxeBcMode->RouteTable[Index].SubnetMask
+ )) {
+ return &PxeBcMode->RouteTable[Index].GwAddr;
+ }
+ }
+
+ return NULL;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// routine to send ipv4 packet
+// ipv4 header of length HdrLth in TransmitBufferPtr
+// routine fills in ipv4hdr Ver_Hdl, TotalLength, and Checksum, moves in Data
+// and gets dest MAC address
+//
+#define IP_TX_BUFFER ((IPV4_BUFFER *) Private->TransmitBufferPtr)
+#define IP_TX_HEADER IP_TX_BUFFER->IpHeader
+
+EFI_STATUS
+Ipv4Xmt (
+ PXE_BASECODE_DEVICE *Private,
+ UINT32 GatewayIp,
+ UINTN IpHeaderLength,
+ UINTN TotalHeaderLength,
+ VOID *Data,
+ UINTN DataLength,
+ EFI_PXE_BASE_CODE_FUNCTION Function
+ )
+{
+ EFI_MAC_ADDRESS DestMac;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ EFI_STATUS StatCode;
+ UINTN PacketLength;
+
+ Snp = Private->SimpleNetwork;
+ PxeBcMode = Private->EfiBc.Mode;
+ StatCode = EFI_SUCCESS;
+ PacketLength = TotalHeaderLength + DataLength;
+
+ //
+ // get dest MAC address
+ // multicast - convert to hw equiv
+ // unicast on same net, use arp
+ // on different net, arp for router
+ //
+ if (IP_TX_HEADER.DestAddr.L == BROADCAST_IPv4) {
+ CopyMem (&DestMac, &Snp->Mode->BroadcastAddress, sizeof (DestMac));
+ } else if (IS_MULTICAST (&IP_TX_HEADER.DestAddr)) {
+ StatCode = (*Snp->MCastIpToMac) (Snp, PxeBcMode->UsingIpv6, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr, &DestMac);
+ } else {
+ UINT32 Ip;
+
+ if (OnSameSubnet (
+ Private->IpLength,
+ &PxeBcMode->StationIp,
+ (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr,
+ &PxeBcMode->SubnetMask
+ )) {
+ Ip = IP_TX_HEADER.DestAddr.L;
+ } else if (GatewayIp != 0) {
+ Ip = GatewayIp;
+ } else {
+ EFI_IP_ADDRESS *TmpIp;
+
+ TmpIp = GetRouterIp (Private, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr);
+
+ if (TmpIp == NULL) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nIpv4Xmit() Exit #1 %xh (%r)",
+ EFI_NO_RESPONSE,
+ EFI_NO_RESPONSE)
+ );
+
+ return EFI_NO_RESPONSE;
+ //
+ // no router
+ //
+ }
+
+ Ip = TmpIp->Addr[0];
+ }
+
+ if (!GetHwAddr (
+ Private,
+ (EFI_IP_ADDRESS *) &Ip,
+ (EFI_MAC_ADDRESS *) &DestMac
+ )) {
+ if (!PxeBcMode->AutoArp) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nIpv4Xmit() Exit #2 %xh (%r)",
+ EFI_DEVICE_ERROR,
+ EFI_DEVICE_ERROR)
+ );
+
+ return EFI_DEVICE_ERROR;
+ } else {
+ StatCode = DoArp (
+ Private,
+ (EFI_IP_ADDRESS *) &Ip,
+ (EFI_MAC_ADDRESS *) &DestMac
+ );
+ }
+ }
+ }
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG ((DEBUG_WARN, "\nIpv4Xmit() Exit #3 %xh (%r)", StatCode, StatCode));
+ return StatCode;
+ }
+ //
+ // fill in packet info
+ //
+ SET_IPV4_VER_HDL (&IP_TX_HEADER, IpHeaderLength);
+ IP_TX_HEADER.TotalLength = HTONS (PacketLength);
+ IP_TX_HEADER.HeaderChecksum = IpChecksum ((UINT16 *) &IP_TX_HEADER, IpHeaderLength);
+ CopyMem (((UINT8 *) &IP_TX_HEADER) + TotalHeaderLength, Data, DataLength);
+
+ //
+ // send it
+ //
+ return SendPacket (
+ Private,
+ (UINT8 *) &IP_TX_HEADER - Snp->Mode->MediaHeaderSize,
+ &IP_TX_HEADER,
+ PacketLength,
+ &DestMac,
+ PXE_PROTOCOL_ETHERNET_IP,
+ Function
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// send ipv4 packet with option
+//
+EFI_STATUS
+Ipv4SendWOp (
+ PXE_BASECODE_DEVICE *Private,
+ UINT32 GatewayIp,
+ UINT8 *Msg,
+ UINTN MessageLength,
+ UINT8 Prot,
+ UINT8 *Option,
+ UINTN OptionLength,
+ UINT32 DestIp,
+ EFI_PXE_BASE_CODE_FUNCTION Function
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ UINTN HdrLth;
+
+ PxeBcMode = Private->EfiBc.Mode;
+ HdrLth = sizeof (IPV4_HEADER) + OptionLength;
+
+ ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER));
+ IP_TX_HEADER.TimeToLive = PxeBcMode->TTL;
+ IP_TX_HEADER.TypeOfService = PxeBcMode->ToS;
+ IP_TX_HEADER.Protocol = Prot;
+ IP_TX_HEADER.SrcAddr.L = *(UINT32 *) &PxeBcMode->StationIp;
+ IP_TX_HEADER.DestAddr.L = DestIp;
+ IP_TX_HEADER.Id = Random (Private);
+ CopyMem (IP_TX_BUFFER->u.Data, Option, OptionLength);
+ return Ipv4Xmt (
+ Private,
+ GatewayIp,
+ HdrLth,
+ HdrLth,
+ Msg,
+ MessageLength,
+ Function
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// send MessageLength message at MessagePtr - higher level protocol header already in TransmitBufferPtr, length HdrSize
+//
+EFI_STATUS
+Ip4Send (
+ PXE_BASECODE_DEVICE *Private, // pointer to instance data
+ UINTN MayFrag, //
+ UINT8 Prot, // protocol
+ UINT32 SrcIp, // Source IP address
+ UINT32 DestIp, // Destination IP address
+ UINT32 GatewayIp, // used if not NULL and needed
+ UINTN HdrSize, // protocol header byte length
+ UINT8 *MessagePtr, // pointer to data
+ UINTN MessageLength // data byte length
+ )
+{
+ EFI_STATUS StatCode;
+ UINTN TotDataLength;
+
+ TotDataLength = HdrSize + MessageLength;
+
+ if (TotDataLength > MAX_IPV4_DATA_SIZE) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nIp4Send() Exit #1 %xh (%r)",
+ EFI_BAD_BUFFER_SIZE,
+ EFI_BAD_BUFFER_SIZE)
+ );
+
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER));
+ IP_TX_HEADER.TimeToLive = DEFAULT_TTL;
+ IP_TX_HEADER.Protocol = Prot;
+ IP_TX_HEADER.SrcAddr.L = SrcIp;
+ IP_TX_HEADER.DestAddr.L = DestIp;
+ IP_TX_HEADER.Id = Random (Private);
+
+ if (!MayFrag) {
+ *(UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_NO_FRAG >> 8;
+ }
+ //
+ // check for need to fragment
+ //
+ if (TotDataLength > MAX_IPV4_FRAME_DATA_SIZE) {
+ UINTN DataLengthSent;
+ UINT16 FragmentOffset;
+
+ FragmentOffset = IP_MORE_FRAG;
+ //
+ // frag offset field
+ //
+ if (!MayFrag) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nIp4Send() Exit #2 %xh (%r)",
+ EFI_BAD_BUFFER_SIZE,
+ EFI_BAD_BUFFER_SIZE)
+ );
+
+ return EFI_BAD_BUFFER_SIZE;
+ }
+ //
+ // send out in fragments - first includes upper level header
+ // all are max and include more frag bit except last
+ //
+ * (UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_MORE_FRAG >> 8;
+
+#define IPV4_FRAG_SIZE (MAX_IPV4_FRAME_DATA_SIZE & 0xfff8)
+#define IPV4_FRAG_OFF_INC (IPV4_FRAG_SIZE >> 3)
+
+ DataLengthSent = IPV4_FRAG_SIZE - HdrSize;
+
+ StatCode = Ipv4Xmt (
+ Private,
+ GatewayIp,
+ sizeof (IPV4_HEADER),
+ sizeof (IPV4_HEADER) + HdrSize,
+ MessagePtr,
+ DataLengthSent,
+ Private->Function
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nIp4Send() Exit #3 %xh (%r)",
+ StatCode,
+ StatCode)
+ );
+
+ return StatCode;
+ }
+
+ MessagePtr += DataLengthSent;
+ MessageLength -= DataLengthSent;
+ FragmentOffset += IPV4_FRAG_OFF_INC;
+ IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset);
+
+ while (MessageLength > IPV4_FRAG_SIZE) {
+ StatCode = Ipv4Xmt (
+ Private,
+ GatewayIp,
+ sizeof (IPV4_HEADER),
+ sizeof (IPV4_HEADER),
+ MessagePtr,
+ IPV4_FRAG_SIZE,
+ Private->Function
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nIp4Send() Exit #3 %xh (%r)",
+ StatCode,
+ StatCode)
+ );
+
+ return StatCode;
+ }
+
+ MessagePtr += IPV4_FRAG_SIZE;
+ MessageLength -= IPV4_FRAG_SIZE;
+ FragmentOffset += IPV4_FRAG_OFF_INC;
+ IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset);
+ }
+
+ * (UINT8 *) (&IP_TX_HEADER.FragmentFields) &= ~(IP_MORE_FRAG >> 8);
+ HdrSize = 0;
+ }
+ //
+ // transmit
+ //
+ return Ipv4Xmt (
+ Private,
+ GatewayIp,
+ sizeof (IPV4_HEADER),
+ sizeof (IPV4_HEADER) + HdrSize,
+ MessagePtr,
+ MessageLength,
+ Private->Function
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// return true if dst IP in receive header matched with what's enabled
+//
+STATIC
+BOOLEAN
+IPgood (
+ PXE_BASECODE_DEVICE *Private,
+ IPV4_HEADER *IpHeader
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ UINTN Index;
+
+ PxeBcMode = Private->EfiBc.Mode;
+
+ if (PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) {
+ return TRUE;
+ }
+
+ if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) &&
+ IS_MULTICAST (&IpHeader->DestAddr)
+ ) {
+ return TRUE;
+ }
+
+ if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) &&
+ PxeBcMode->StationIp.Addr[0] == IpHeader->DestAddr.L
+ ) {
+ return TRUE;
+ }
+
+ if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) && IpHeader->DestAddr.L == BROADCAST_IPv4) {
+ return TRUE;
+ }
+
+ for (Index = 0; Index < PxeBcMode->IpFilter.IpCnt; ++Index) {
+ if (IpHeader->DestAddr.L == PxeBcMode->IpFilter.IpList[Index].Addr[0]) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// receive up to MessageLength message into MessagePtr for protocol Prot
+// return message length, src/dest ips if select any, and pointer to protocol
+// header routine will filter based on source and/or dest ip if OpFlags set.
+//
+EFI_STATUS
+IpReceive (
+ PXE_BASECODE_DEVICE *Private,
+ PXE_OPFLAGS OpFlags,
+ EFI_IP_ADDRESS *SrcIpPtr,
+ EFI_IP_ADDRESS *DestIpPtr,
+ UINT8 Prot,
+ VOID *HeaderPtr,
+ UINTN HdrSize,
+ UINT8 *MessagePtr,
+ UINTN *MessageLengthPtr,
+ EFI_EVENT TimeoutEvent
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ EFI_STATUS StatCode;
+ UINTN ByteCount;
+ UINTN FragmentCount;
+ UINTN ExpectedPacketLength;
+ UINTN Id;
+ BOOLEAN GotFirstFragment;
+ BOOLEAN GotLastFragment;
+
+ DEBUG (
+ (DEBUG_NET,
+ "\nIpReceive() Hdr=%Xh HdrSz=%d Data=%Xh DataSz=%d",
+ HeaderPtr,
+ HdrSize,
+ MessagePtr,
+ *MessageLengthPtr)
+ );
+
+ PxeBcMode = Private->EfiBc.Mode;
+ PxeBcMode->IcmpErrorReceived = FALSE;
+
+ ExpectedPacketLength = 0;
+ GotFirstFragment = FALSE;
+ GotLastFragment = FALSE;
+ FragmentCount = 0;
+ ByteCount = 0;
+ Id = 0;
+
+ for (;;) {
+ IPV4_HEADER IpHdr;
+ UINTN FFlds;
+ UINTN TotalLength;
+ UINTN FragmentOffset;
+ UINTN HeaderSize;
+ UINTN BufferSize;
+ UINTN IpHeaderLength;
+ UINTN DataLength;
+ UINT16 Protocol;
+ UINT8 *NextHdrPtr;
+ UINT8 *PacketPtr;
+
+ StatCode = WaitForReceive (
+ Private,
+ Private->Function,
+ TimeoutEvent,
+ &HeaderSize,
+ &BufferSize,
+ &Protocol
+ );
+
+ if (EFI_ERROR (StatCode)) {
+ return StatCode;
+ }
+
+ PacketPtr = Private->ReceiveBufferPtr + HeaderSize;
+
+ if (Protocol == PXE_PROTOCOL_ETHERNET_ARP) {
+ HandleArpReceive (
+ Private,
+ (ARP_PACKET *) PacketPtr,
+ Private->ReceiveBufferPtr
+ );
+
+ continue;
+ }
+
+ if (Protocol != PXE_PROTOCOL_ETHERNET_IP) {
+ continue;
+ }
+
+#define IpRxHeader ((IPV4_HEADER *) PacketPtr)
+
+ //
+ // filter for version & check sum
+ //
+ IpHeaderLength = IPV4_HEADER_LENGTH (IpRxHeader);
+
+ if ((IpRxHeader->VersionIhl >> 4) != IPVER4) {
+ continue;
+ }
+
+ if (IpChecksum ((UINT16 *) IpRxHeader, IpHeaderLength)) {
+ continue;
+ }
+
+ CopyMem (&IpHdr, IpRxHeader, sizeof (IpHdr));
+ TotalLength = NTOHS (IpHdr.TotalLength);
+
+ if (IpHdr.Protocol == PROT_TCP) {
+ //
+ // The NextHdrPtr is used to seed the header buffer we are passing back.
+ // That being the case, we want to see everything in pPkt which contains
+ // everything but the ethernet (or whatever) frame. IP + TCP in this case.
+ //
+ DataLength = TotalLength;
+ NextHdrPtr = PacketPtr;
+ } else {
+ DataLength = TotalLength - IpHeaderLength;
+ NextHdrPtr = PacketPtr + IpHeaderLength;
+ }
+ //
+ // If this is an ICMP, it might not be for us.
+ // Double check the state of the IP stack and the
+ // packet fields before assuming it is an ICMP
+ // error. ICMP requests are not supported by the
+ // PxeBc IP stack and should be ignored.
+ //
+ if (IpHdr.Protocol == PROT_ICMP) {
+ ICMPV4_HEADER *Icmpv4;
+
+ Icmpv4 = (ICMPV4_HEADER *) NextHdrPtr;
+
+ //
+ // For now only obvious ICMP error replies will be accepted by
+ // this stack. This still makes us vulnerable to DoS attacks.
+ // But at least we will not be killed by DHCP daemons.
+ //
+ switch (Icmpv4->Type) {
+ case ICMP_REDIRECT:
+ case ICMP_ECHO:
+ case ICMP_ROUTER_ADV:
+ case ICMP_ROUTER_SOLICIT:
+ case ICMP_TIMESTAMP:
+ case ICMP_TIMESTAMP_REPLY:
+ case ICMP_INFO_REQ:
+ case ICMP_INFO_REQ_REPLY:
+ case ICMP_SUBNET_MASK_REQ:
+ case ICMP_SUBNET_MASK_REPLY:
+ default:
+ continue;
+
+ //
+ // %%TBD - This should be implemented.
+ //
+ case ICMP_ECHO_REPLY:
+ continue;
+
+ case ICMP_DEST_UNREACHABLE:
+ case ICMP_TIME_EXCEEDED:
+ case ICMP_PARAMETER_PROBLEM:
+ case ICMP_SOURCE_QUENCH:
+ PxeBcMode->IcmpErrorReceived = TRUE;
+
+ CopyMem (
+ &PxeBcMode->IcmpError,
+ NextHdrPtr,
+ sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
+ );
+
+ DEBUG (
+ (DEBUG_NET,
+ "\nIpReceive() Exit #1 %Xh (%r)",
+ EFI_ICMP_ERROR,
+ EFI_ICMP_ERROR)
+ );
+ }
+
+ return EFI_ICMP_ERROR;
+ }
+
+ if (IpHdr.Protocol == PROT_IGMP) {
+ HandleIgmp (Private, (IGMPV2_MESSAGE *) NextHdrPtr, DataLength);
+
+ DEBUG ((DEBUG_NET, "\n IGMP"));
+ continue;
+ }
+ //
+ // check for protocol
+ //
+ if (IpHdr.Protocol != Prot) {
+ continue;
+ }
+ //
+ // do filtering
+ //
+ if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr && SrcIpPtr->Addr[0] != IpHdr.SrcAddr.L) {
+ DEBUG ((DEBUG_NET, "\n Not expected source IP address."));
+ continue;
+ }
+
+ if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) {
+ if (!IPgood (Private, &IpHdr)) {
+ continue;
+ }
+ } else if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP)) {
+ if (DestIpPtr == NULL) {
+ if (PxeBcMode->StationIp.Addr[0] != IpHdr.DestAddr.L) {
+ continue;
+ }
+ } else if (DestIpPtr->Addr[0] != IpHdr.DestAddr.L) {
+ continue;
+ }
+ }
+ //
+ // get some data we need
+ //
+ FFlds = NTOHS (IpHdr.FragmentFields);
+ FragmentOffset = ((FFlds & IP_FRAG_OFF_MSK) << 3);
+
+ /* Keep count of fragments that belong to this session.
+ * If we get packets with a different IP ID number,
+ * ignore them. Ignored packets should be handled
+ * by the upper level protocol.
+ */
+ if (FragmentCount == 0) {
+ Id = IpHdr.Id;
+
+ if (DestIpPtr != NULL) {
+ DestIpPtr->Addr[0] = IpHdr.DestAddr.L;
+ }
+
+ if (SrcIpPtr != NULL) {
+ SrcIpPtr->Addr[0] = IpHdr.SrcAddr.L;
+ }
+ } else {
+ if (IpHdr.Id != Id) {
+ continue;
+ }
+ }
+
+ ++FragmentCount;
+
+ /* Fragment management.
+ */
+ if (FragmentOffset == 0) {
+ /* This is the first fragment (may also be the
+ * only fragment).
+ */
+ GotFirstFragment = TRUE;
+
+ /* If there is a separate protocol header buffer,
+ * copy the header, adjust the data pointer and
+ * the data length.
+ */
+ if (HdrSize != 0) {
+ CopyMem (HeaderPtr, NextHdrPtr, HdrSize);
+
+ NextHdrPtr += HdrSize;
+ DataLength -= HdrSize;
+ }
+ } else {
+ /* If there is a separate protocol header buffer,
+ * adjust the fragment offset.
+ */
+ FragmentOffset -= HdrSize;
+ }
+
+ /* See if this is the last fragment.
+ */
+ if (!(FFlds & IP_MORE_FRAG)) {
+ //
+ // This is the last fragment (may also be the only fragment).
+ //
+ GotLastFragment = TRUE;
+
+ /* Compute the expected length of the assembled
+ * packet. This will be used to decide if we
+ * have gotten all of the fragments.
+ */
+ ExpectedPacketLength = FragmentOffset + DataLength;
+ }
+
+ DEBUG (
+ (DEBUG_NET,
+ "\n ID = %Xh Off = %d Len = %d",
+ Id,
+ FragmentOffset,
+ DataLength)
+ );
+
+ /* Check for receive buffer overflow.
+ */
+ if (FragmentOffset + DataLength > *MessageLengthPtr) {
+ /* There is not enough space in the receive
+ * buffer for the fragment.
+ */
+ DEBUG (
+ (DEBUG_NET,
+ "\nIpReceive() Exit #3 %Xh (%r)",
+ EFI_BUFFER_TOO_SMALL,
+ EFI_BUFFER_TOO_SMALL)
+ );
+
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ /* Copy data into receive buffer.
+ */
+ if (DataLength != 0) {
+ DEBUG ((DEBUG_NET, " To = %Xh", MessagePtr + FragmentOffset));
+
+ CopyMem (MessagePtr + FragmentOffset, NextHdrPtr, DataLength);
+ ByteCount += DataLength;
+ }
+
+ /* If we have seen the first and last fragments and
+ * the receive byte count is at least as large as the
+ * expected byte count, return SUCCESS.
+ *
+ * We could be tricked by receiving a fragment twice
+ * but the upper level protocol should figure this
+ * out.
+ */
+ if (GotFirstFragment && GotLastFragment && ByteCount >= ExpectedPacketLength) {
+ *MessageLengthPtr = ExpectedPacketLength;
+ return EFI_SUCCESS;
+ }
+ }
+}
+
+/* eof - pxe_bc_ip.c */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c
new file mode 100644
index 0000000000..43e1bbb429
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c
@@ -0,0 +1,2193 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ pxe_bc_mtftp.c
+
+Abstract:
+ TFTP and MTFTP (multicast TFTP) implementation.
+
+Revision History
+
+
+**/
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// The following #define is used to create a version that does not wait to
+// open after a listen. This is just for a special regression test of MTFTP
+// server to make sure multiple opens are handled correctly. Normally this
+// next line should be a comment.
+// #define SpecialNowaitVersion // comment out for normal operation
+//
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+#include "bc.h"
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+UINT64
+Swap64 (
+ UINT64 n
+ )
+{
+ union {
+ UINT64 n;
+ UINT8 b[8];
+ } u;
+
+ UINT8 t;
+
+ u.n = n;
+
+ t = u.b[0];
+ u.b[0] = u.b[7];
+ u.b[7] = t;
+
+ t = u.b[1];
+ u.b[1] = u.b[6];
+ u.b[6] = t;
+
+ t = u.b[2];
+ u.b[2] = u.b[5];
+ u.b[5] = t;
+
+ t = u.b[3];
+ u.b[3] = u.b[4];
+ u.b[4] = t;
+
+ return u.n;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return EFI_SUCCESS :=
+ @return EFI_TFTP_ERROR :=
+ @return other :=
+
+**/
+STATIC
+EFI_STATUS
+TftpUdpRead (
+ PXE_BASECODE_DEVICE *Private,
+ UINT16 Operation,
+ VOID *HeaderPtr,
+ UINTN *BufferSizePtr,
+ VOID *BufferPtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
+ EFI_IP_ADDRESS *OurIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
+ UINT16 Timeout
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ EFI_STATUS Status;
+ EFI_EVENT TimeoutEvent;
+ UINTN HeaderSize;
+
+ //
+ //
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ Timeout * 10000000 + 1000000
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return Status;
+ }
+ //
+ //
+ //
+ HeaderSize = Private->BigBlkNumFlag ? sizeof (struct Tftpv4Ack8) : sizeof (struct Tftpv4Ack);
+
+#define ERROR_MESSAGE_PTR ((struct Tftpv4Error *) HeaderPtr)
+
+ Status = UdpRead (
+ Private,
+ Operation,
+ OurIpPtr,
+ OurPortPtr,
+ ServerIpPtr,
+ ServerPortPtr,
+ &HeaderSize,
+ HeaderPtr,
+ BufferSizePtr,
+ BufferPtr,
+ TimeoutEvent
+ );
+
+ if (Status != EFI_SUCCESS || ERROR_MESSAGE_PTR->OpCode != HTONS (TFTP_ERROR)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return Status;
+ }
+ //
+ // got an error packet
+ // write one byte error code followed by error message
+ //
+ PxeBcMode = Private->EfiBc.Mode;
+ PxeBcMode->TftpErrorReceived = TRUE;
+ PxeBcMode->TftpError.ErrorCode = (UINT8) NTOHS (ERROR_MESSAGE_PTR->ErrCode);
+ HeaderSize = MIN (*BufferSizePtr, sizeof PxeBcMode->TftpError.ErrorString);
+ CopyMem (PxeBcMode->TftpError.ErrorString, BufferPtr, HeaderSize);
+
+ gBS->CloseEvent (TimeoutEvent);
+ return EFI_TFTP_ERROR;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+VOID
+SendError (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr
+ )
+{
+ struct Tftpv4Error *ErrStr;
+ UINTN Len;
+
+ ErrStr = (VOID *) Private->TftpErrorBuffer;
+ Len = sizeof *ErrStr;
+
+ ErrStr->OpCode = HTONS (TFTP_ERROR);
+ ErrStr->ErrCode = HTONS (TFTP_ERR_OPTION);
+ ErrStr->ErrMsg[0] = 0;
+
+ UdpWrite (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
+ ServerIpPtr,
+ ServerPortPtr,
+ 0,
+ 0,
+ OurPortPtr,
+ 0,
+ 0,
+ &Len,
+ ErrStr
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+EFI_STATUS
+SendAckAndGetData (
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
+ EFI_IP_ADDRESS *ReplyIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
+ UINT16 Timeout,
+ UINTN *ReplyLenPtr,
+ UINT8 *PxeBcMode,
+ UINT64 *BlockNumPtr,
+ BOOLEAN AckOnly
+ )
+{
+ struct Tftpv4Data DataBuffer;
+ struct Tftpv4Ack *Ack2Ptr;
+ struct Tftpv4Ack8 *Ack8Ptr;
+ EFI_STATUS Status;
+ UINTN Len;
+
+ Ack2Ptr = (VOID *) Private->TftpAckBuffer;
+ Ack8Ptr = (VOID *) Private->TftpAckBuffer;
+
+ if (Private->BigBlkNumFlag) {
+ Len = sizeof (struct Tftpv4Ack8);
+
+ Ack8Ptr->OpCode = HTONS (TFTP_ACK8);
+ Ack8Ptr->BlockNum = Swap64 (*BlockNumPtr);
+
+ Status = UdpWrite (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
+ ServerIpPtr,
+ ServerPortPtr,
+ 0,
+ 0,
+ OurPortPtr,
+ 0,
+ 0,
+ &Len,
+ Ack8Ptr
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ Len = sizeof (struct Tftpv4Ack);
+
+ Ack2Ptr->OpCode = HTONS (TFTP_ACK);
+ Ack2Ptr->BlockNum = HTONS ((UINT16) *BlockNumPtr);
+
+ Status = UdpWrite (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
+ ServerIpPtr,
+ ServerPortPtr,
+ 0,
+ 0,
+ OurPortPtr,
+ 0,
+ 0,
+ &Len,
+ Ack2Ptr
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (AckOnly) {
+ //
+ // ACK of last packet. This is just a courtesy.
+ // Do not wait for response.
+ //
+ return EFI_SUCCESS;
+ }
+ //
+ // read reply
+ //
+ Status = TftpUdpRead (
+ Private,
+ 0,
+ &DataBuffer,
+ ReplyLenPtr,
+ PxeBcMode,
+ ServerIpPtr,
+ ServerPortPtr,
+ ReplyIpPtr,
+ OurPortPtr,
+ Timeout
+ );
+
+ if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ return Status;
+ }
+ //
+ // got a good reply (so far)
+ // check for next data packet
+ //
+ if (!Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA)) {
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);
+ }
+
+ *BlockNumPtr = NTOHS (DataBuffer.Header.BlockNum);
+ return Status;
+ }
+
+ if (Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA8)) {
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);
+ }
+
+ *BlockNumPtr = Swap64 (*(UINT64 *) &DataBuffer.Header.BlockNum);
+ return Status;
+ }
+
+ return EFI_PROTOCOL_ERROR;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+EFI_STATUS
+LockStepReceive (
+ PXE_BASECODE_DEVICE *Private,
+ UINTN PacketSize,
+ UINT64 *BufferSizePtr,
+ UINT64 Offset,
+ UINT8 *BufferPtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
+ EFI_IP_ADDRESS *ReplyIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
+ UINT64 LastBlock,
+ UINT16 Timeout,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_STATUS Status;
+ UINT64 BlockNum;
+ UINT64 BufferSize;
+ UINTN Retries;
+ UINTN SaveLen;
+ UINTN ReplyLen;
+
+ ReplyLen = PacketSize;
+ BlockNum = LastBlock;
+
+ DEBUG ((DEBUG_INFO, "\nLockStepReceive() PacketSize = %d", PacketSize));
+
+ if (DontUseBuffer) {
+ BufferSize = PacketSize;
+ } else {
+ BufferSize = *BufferSizePtr - Offset;
+ BufferPtr += Offset;
+ }
+
+ while (ReplyLen >= 512 && ReplyLen == PacketSize) {
+ if (BufferSize < PacketSize) {
+ ReplyLen = (UINTN) ((BufferSize > 0) ? BufferSize : 0);
+ }
+
+ SaveLen = ReplyLen;
+
+ //
+ // write an ack packet and get data - retry up to NUM_ACK_RETRIES on timeout
+ //
+ Retries = NUM_ACK_RETRIES;
+
+ do {
+ ReplyLen = SaveLen;
+
+ Status = SendAckAndGetData (
+ Private,
+ ServerIpPtr,
+ ServerPortPtr,
+ ReplyIpPtr,
+ OurPortPtr,
+ Timeout,
+ (UINTN *) &ReplyLen,
+ BufferPtr,
+ &BlockNum,
+ FALSE
+ );
+
+ if (!EFI_ERROR (Status) || Status == EFI_BUFFER_TOO_SMALL) {
+ if (BlockNum == LastBlock) {
+ DEBUG ((DEBUG_NET, "\nresend"));
+ //
+ // a resend - continue
+ //
+ Status = EFI_TIMEOUT;
+ } else if (Private->BigBlkNumFlag) {
+ if (BlockNum != ++LastBlock) {
+ DEBUG ((DEBUG_NET, "\nLockStepReceive() Exit #1a"));
+ //
+ // not correct blocknum - error
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ } else {
+ LastBlock = (LastBlock + 1) & 0xFFFF;
+ if (BlockNum != LastBlock) {
+ DEBUG ((DEBUG_NET, "\nLockStepReceive() Exit #1b"));
+ return EFI_PROTOCOL_ERROR;
+ //
+ // not correct blocknum - error
+ //
+ }
+ }
+ }
+ } while (Status == EFI_TIMEOUT && --Retries);
+
+ if (EFI_ERROR (Status)) {
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);
+ }
+
+ return Status;
+ }
+
+ if (DontUseBuffer) {
+ BufferSize += ReplyLen;
+ } else {
+ BufferPtr += ReplyLen;
+ BufferSize -= ReplyLen;
+ }
+ }
+ //
+ // while (ReplyLen == PacketSize);
+ //
+ if (DontUseBuffer) {
+ if (BufferSizePtr != NULL) {
+ *BufferSizePtr = (BufferSize - PacketSize);
+ }
+ } else {
+ *BufferSizePtr -= BufferSize;
+ }
+
+ /* Send ACK of last packet. */
+ ReplyLen = 0;
+
+ SendAckAndGetData (
+ Private,
+ ServerIpPtr,
+ ServerPortPtr,
+ ReplyIpPtr,
+ OurPortPtr,
+ Timeout,
+ (UINTN *) &ReplyLen,
+ BufferPtr,
+ &BlockNum,
+ TRUE
+ );
+
+ return EFI_SUCCESS;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// some literals
+//
+STATIC UINT8 Mode[] = MODE_BINARY;
+STATIC UINT8 BlockSizeOp[] = OP_BLKSIZE;
+STATIC UINT8 TsizeOp[] = OP_TFRSIZE;
+STATIC UINT8 OverwriteOp[] = OP_OVERWRITE;
+STATIC UINT8 BigBlkNumOp[] = OP_BIGBLKNUM;
+STATIC EFI_PXE_BASE_CODE_UDP_PORT TftpRequestPort = TFTP_OPEN_PORT;
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return Pointer to value field if option found or NULL if not found.
+
+**/
+STATIC
+UINT8 *
+FindOption (
+ UINT8 *OptionPtr,
+ INTN OpLen,
+ UINT8 *OackPtr,
+ INTN OackSize
+ )
+{
+ if ((OackSize -= OpLen) <= 0) {
+ return NULL;
+ }
+
+ do {
+ if (!CompareMem (OackPtr, OptionPtr, OpLen)) {
+ return OackPtr + OpLen;
+ }
+
+ ++OackPtr;
+ } while (--OackSize);
+
+ return NULL;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+#define BKSZOP 1 // block size
+#define TSIZEOP 2 // transfer size
+#define OVERWRITEOP 4 // overwrite
+#define BIGBLKNUMOP 8 // big block numbers
+STATIC
+EFI_STATUS
+TftpRwReq (
+ UINT16 Req,
+ UINT16 Options,
+ PXE_BASECODE_DEVICE *Private,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
+ UINT8 *FilenamePtr,
+ UINTN *PacketSizePtr,
+ VOID *Buffer
+ )
+{
+ union {
+ UINT8 Data[514];
+ struct Tftpv4Req ReqStr;
+ } *u;
+
+ UINT16 OpFlags;
+ INTN Len;
+ INTN TotalLen;
+ UINT8 *Ptr;
+
+ if (*OurPortPtr == 0) {
+ OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT;
+ } else {
+ OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT;
+ }
+ //
+ // build the basic request - opcode, filename, mode
+ //
+ u = Buffer;
+ u->ReqStr.OpCode = HTONS (Req);
+ TotalLen = sizeof (Mode) + sizeof (u->ReqStr.OpCode) + (Len = 1 + AsciiStrLen (FilenamePtr));
+
+ CopyMem (u->ReqStr.FileName, FilenamePtr, Len);
+ Ptr = (UINT8 *) (u->ReqStr.FileName + Len);
+
+ CopyMem (Ptr, Mode, sizeof (Mode));
+ Ptr += sizeof (Mode);
+
+ if (Options & BKSZOP) {
+ CopyMem (Ptr, BlockSizeOp, sizeof (BlockSizeOp));
+ UtoA10 (*PacketSizePtr, Ptr + sizeof (BlockSizeOp));
+
+ TotalLen += (Len = 1 + AsciiStrLen (Ptr + sizeof (BlockSizeOp)) + sizeof (BlockSizeOp));
+
+ Ptr += Len;
+ }
+
+ if (Options & TSIZEOP) {
+ CopyMem (Ptr, TsizeOp, sizeof (TsizeOp));
+ CopyMem (Ptr + sizeof (TsizeOp), "0", 2);
+ TotalLen += sizeof (TsizeOp) + 2;
+ Ptr += sizeof (TsizeOp) + 2;
+ }
+
+ if (Options & OVERWRITEOP) {
+ CopyMem (Ptr, OverwriteOp, sizeof (OverwriteOp));
+ CopyMem (Ptr + sizeof (OverwriteOp), "1", 2);
+ TotalLen += sizeof (OverwriteOp) + 2;
+ Ptr += sizeof (OverwriteOp) + 2;
+ }
+
+ if (Options & BIGBLKNUMOP) {
+ CopyMem (Ptr, BigBlkNumOp, sizeof (BigBlkNumOp));
+ CopyMem (Ptr + sizeof (BigBlkNumOp), "8", 2);
+ TotalLen += sizeof (BigBlkNumOp) + 2;
+ Ptr += sizeof (BigBlkNumOp) + 2;
+ }
+ //
+ // send it
+ //
+ return UdpWrite (
+ Private,
+ OpFlags,
+ ServerIpPtr,
+ ServerPortPtr,
+ 0,
+ 0,
+ OurPortPtr,
+ 0,
+ 0,
+ (UINTN *) &TotalLen,
+ u
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+EFI_STATUS
+TftpRwReqwResp (
+ UINT16 Req,
+ UINT16 Options,
+ PXE_BASECODE_DEVICE *Private,
+ VOID *HeaderPtr,
+ UINTN *PacketSizePtr,
+ UINTN *ReplyLenPtr,
+ VOID *BufferPtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *ServerReplyPortPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
+ UINT8 *FilenamePtr,
+ UINT16 Timeout
+ )
+{
+ EFI_STATUS Status;
+ UINTN SaveReplyLen;
+ INTN Retries;
+ UINT8 Buffer[514];
+
+ SaveReplyLen = *ReplyLenPtr;
+ Retries = 3;
+ Private->BigBlkNumFlag = FALSE;
+ *OurPortPtr = 0;
+ //
+ // generate random
+ //
+ do {
+ if (*OurPortPtr != 0) {
+ if (++ *OurPortPtr == 0) {
+ *OurPortPtr = PXE_RND_PORT_LOW;
+ }
+ }
+ //
+ // send request from our Ip = StationIp
+ //
+ if ((Status = TftpRwReq (
+ Req,
+ Options,
+ Private,
+ ServerIpPtr,
+ ServerPortPtr,
+ OurPortPtr,
+ FilenamePtr,
+ PacketSizePtr,
+ Buffer
+ )) != EFI_SUCCESS) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nTftpRwReqwResp() Exit #1 %xh (%r)",
+ Status,
+ Status)
+ );
+
+ return Status;
+ }
+ //
+ // read reply to our Ip = StationIp
+ //
+ *ReplyLenPtr = SaveReplyLen;
+
+ Status = TftpUdpRead (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
+ HeaderPtr,
+ ReplyLenPtr,
+ BufferPtr,
+ ServerIpPtr,
+ ServerReplyPortPtr,
+ 0,
+ OurPortPtr,
+ Timeout
+ );
+ } while (Status == EFI_TIMEOUT && --Retries);
+
+ if (!Options || Status != EFI_TFTP_ERROR) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nTftpRwReqwResp() Exit #2 %xh (%r)",
+ Status,
+ Status)
+ );
+ return Status;
+ }
+
+ Status = TftpRwReqwResp (
+ Req,
+ 0,
+ Private,
+ HeaderPtr,
+ PacketSizePtr,
+ ReplyLenPtr,
+ BufferPtr,
+ ServerIpPtr,
+ ServerPortPtr,
+ ServerReplyPortPtr,
+ OurPortPtr,
+ FilenamePtr,
+ Timeout
+ );
+
+ DEBUG ((DEBUG_WARN, "\nTftpRwReqwResp() Exit #3 %xh (%r)", Status, Status));
+
+ return Status;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// mtftp listen
+// read on mcast ip, cport, from sport, for data packet
+// returns success if gets multicast last packet or all up to last block
+// if not missing, then finished
+//
+
+/**
+
+
+**/
+STATIC
+EFI_STATUS
+MtftpListen (
+ PXE_BASECODE_DEVICE *Private,
+ UINT64 *BufferSizePtr,
+ UINT8 *BufferPtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr,
+ UINT64 *StartBlockPtr,
+ UINTN *NumMissedPtr,
+ UINT16 TransTimeout,
+ UINT16 ListenTimeout,
+ UINT64 FinalBlock,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_STATUS Status;
+ struct Tftpv4Ack Header;
+ UINT64 Offset;
+ UINT64 BlockNum;
+ UINT64 LastBlockNum;
+ UINT64 BufferSize;
+ UINTN NumMissed;
+ UINTN PacketSize;
+ UINTN SaveReplyLen;
+ UINTN ReplyLen;
+ UINT16 Timeout;
+
+ LastBlockNum = *StartBlockPtr;
+ Timeout = ListenTimeout;
+ *NumMissedPtr = 0;
+ PacketSize = 0;
+ BufferSize = *BufferSizePtr;
+ ReplyLen = MAX_TFTP_PKT_SIZE;;
+
+ //
+ // receive
+ //
+ do {
+ if ((SaveReplyLen = ReplyLen) > BufferSize) {
+ SaveReplyLen = (UINTN) BufferSize;
+ }
+
+ /* %%TBD - add big block number support */
+
+ //
+ // get data - loop on resends
+ //
+ do {
+ ReplyLen = SaveReplyLen;
+
+ if ((Status = TftpUdpRead (
+ Private,
+ 0,
+ &Header,
+ &ReplyLen,
+ BufferPtr,
+ ServerIpPtr,
+ &MtftpInfoPtr->SPort,
+ &MtftpInfoPtr->MCastIp,
+ &MtftpInfoPtr->CPort,
+ Timeout
+ )) != EFI_SUCCESS) {
+ return Status;
+ }
+ //
+ // make sure a data packet
+ //
+ if (Header.OpCode != HTONS (TFTP_DATA)) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ } while ((BlockNum = NTOHS (Header.BlockNum)) == LastBlockNum);
+
+ //
+ // make sure still going up
+ //
+ if (LastBlockNum > BlockNum) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if (BlockNum - LastBlockNum > 0xFFFFFFFF) {
+ return EFI_PROTOCOL_ERROR;
+ } else {
+ NumMissed = (UINTN) (BlockNum - LastBlockNum - 1);
+ }
+
+ LastBlockNum = BlockNum;
+
+ //
+ // if first time through, some reinitialization
+ //
+ if (!PacketSize) {
+ *StartBlockPtr = BlockNum;
+ PacketSize = ReplyLen;
+ Timeout = TransTimeout;
+ } else {
+ *NumMissedPtr = (UINT16) (*NumMissedPtr + NumMissed);
+ }
+ //
+ // if missed packets, update start block,
+ // etc. and move packet to proper place in buffer
+ //
+ if (NumMissed) {
+ *StartBlockPtr = BlockNum;
+ if (!DontUseBuffer) {
+ Offset = NumMissed * PacketSize;
+ CopyMem (BufferPtr + Offset, BufferPtr, ReplyLen);
+ BufferPtr += Offset;
+ BufferSize -= Offset;
+ }
+ }
+
+ if (!DontUseBuffer) {
+ BufferPtr += ReplyLen;
+ BufferSize -= ReplyLen;
+ }
+ } while (ReplyLen == PacketSize && BlockNum != FinalBlock);
+
+ *BufferSizePtr = BufferSize;
+
+ return EFI_SUCCESS;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return // mtftp open session
+ @return // return code EFI_SUCCESS
+ @return // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done
+ @return // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest
+ @return // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all
+ @retval GOTUNI returns NO_DATA go will go to TFTP session)
+
+**/
+STATIC
+EFI_STATUS
+MtftpOpen (
+ PXE_BASECODE_DEVICE * Private,
+ UINT64 *BufferSizePtr,
+ UINT8 *BufferPtr,
+ UINTN *PacketSizePtr,
+ EFI_IP_ADDRESS * ServerIpPtr,
+ UINT8 *FilenamePtr,
+ EFI_PXE_BASE_CODE_MTFTP_INFO * MtftpInfoPtr,
+ UINT8 *CompletionStatusPtr,
+#define GOTUNI 1
+#define GOTMULTI 2
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP_ADDRESS OurReplyIp;
+ struct Tftpv4Ack Header;
+ INTN ReplyLen;
+ INTN Retries;
+ UINT8 *BufferPtr2;
+ UINT8 TmpBuf[514];
+
+ Retries = NUM_MTFTP_OPEN_RETRIES;
+ BufferPtr2 = BufferPtr;
+ *PacketSizePtr = (UINTN) (MIN (*BufferSizePtr, MAX_TFTP_PKT_SIZE));
+
+ do {
+ //
+ // send a read request
+ //
+ *CompletionStatusPtr = 0;
+
+ if ((Status = TftpRwReq (
+ TFTP_RRQ,
+ 0,
+ Private,
+ ServerIpPtr,
+ &MtftpInfoPtr->SPort,
+ &MtftpInfoPtr->CPort,
+ FilenamePtr,
+ PacketSizePtr,
+ TmpBuf
+ )) != EFI_SUCCESS) {
+ return Status;
+ }
+
+ for (;;) {
+ //
+ // read reply
+ //
+ ZeroMem (&OurReplyIp, Private->IpLength);
+ ReplyLen = *PacketSizePtr;
+
+ if ((Status = TftpUdpRead (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER,
+ &Header,
+ (UINTN *) &ReplyLen,
+ BufferPtr2,
+ ServerIpPtr,
+ &MtftpInfoPtr->SPort,
+ &OurReplyIp,
+ &MtftpInfoPtr->CPort,
+ MtftpInfoPtr->TransmitTimeout
+ )) == EFI_SUCCESS) {
+ //
+ // check for first data packet
+ //
+ if (Header.OpCode != HTONS (TFTP_DATA)) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // check block num
+ //
+ if (Header.BlockNum != HTONS (1)) {
+ //
+ // it's not first
+ // if we are not the primary client,
+ // we probably got first and now second
+ // multicast but no unicast, so
+ // *CompletionStatusPtr = GOTMULTI - if this is
+ // the second, can just go on to listen
+ // starting with 2 as the last block
+ // received
+ //
+ if (Header.BlockNum != HTONS (2)) {
+ //
+ // not second
+ //
+ *CompletionStatusPtr = 0;
+ }
+
+ return Status;
+ }
+
+ //
+ // now actual
+ //
+ *PacketSizePtr = ReplyLen;
+ //
+ // see if a unicast data packet
+ //
+ if (!CompareMem (
+ &OurReplyIp,
+ &Private->EfiBc.Mode->StationIp,
+ Private->IpLength
+ )) {
+ *CompletionStatusPtr |= GOTUNI;
+ //
+ // it is
+ // if already got multicast packet,
+ // got em both
+ //
+ if (*CompletionStatusPtr & GOTMULTI) {
+ break;
+ }
+ } else if (!CompareMem (
+ &OurReplyIp,
+ &MtftpInfoPtr->MCastIp,
+ Private->IpLength
+ )) {
+ //
+ // otherwise see if a multicast data packet
+ //
+ *CompletionStatusPtr |= GOTMULTI;
+ //
+ // it is
+ // got first - bump pointer so that if
+ // second multi comes along, we're OK
+ //
+ if (!DontUseBuffer) {
+ BufferPtr2 = (UINT8 *) BufferPtr + ReplyLen;
+ }
+ //
+ // if already got unicast packet,
+ // got em both
+ //
+ if (*CompletionStatusPtr & GOTUNI) {
+ break;
+ }
+ } else {
+ //
+ // else protocol error
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ } else if (Status == EFI_TIMEOUT) {
+ //
+ // bad return code - if timed out, retry
+ //
+ break;
+ } else {
+ //
+ // else just bad - failed MTFTP open
+ //
+ return Status;
+ }
+ }
+ } while (Status == EFI_TIMEOUT && --Retries);
+
+ if (Status != EFI_SUCCESS) {
+ //
+ // open failed
+ //
+ return Status;
+ }
+ //
+ // got em both - go into receive mode
+ // routine to read rest of file after a successful open (TFTP or MTFTP)
+ // sends ACK and gets next data packet until short packet arrives,
+ // then sends ACK and (hopefully) times out
+ //
+ return LockStepReceive (
+ Private,
+ (UINT16) ReplyLen,
+ BufferSizePtr,
+ ReplyLen,
+ BufferPtr,
+ ServerIpPtr,
+ &MtftpInfoPtr->SPort,
+ &MtftpInfoPtr->MCastIp,
+ &MtftpInfoPtr->CPort,
+ 1,
+ MtftpInfoPtr->TransmitTimeout,
+ DontUseBuffer
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+EFI_STATUS
+MtftpDownload (
+ PXE_BASECODE_DEVICE *Private,
+ UINT64 *BufferSizePtr,
+ UINT8 *BufferPtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ UINT8 *FilenamePtr,
+ EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_PXE_BASE_CODE_IP_FILTER Filter;
+ EFI_STATUS Status;
+ UINT64 StartBlock;
+ UINT64 LastBlock;
+ UINT64 LastStartBlock;
+ UINT64 BufferSize;
+ UINTN Offset;
+ UINTN NumMissed;
+ UINT16 TransTimeout;
+ UINT16 ListenTimeout;
+ UINT8 *BufferPtrLocal;
+
+ TransTimeout = MtftpInfoPtr->TransmitTimeout;
+ ListenTimeout = MtftpInfoPtr->ListenTimeout;
+ LastBlock = 0;
+ LastStartBlock = 0;
+ Offset = 0;
+
+ Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;
+ Filter.IpCnt = 2;
+ Filter.IpList[0] = Private->EfiBc.Mode->StationIp;
+ Filter.IpList[1] = MtftpInfoPtr->MCastIp;
+
+ if ((Status = IpFilter (Private, &Filter)) != EFI_SUCCESS) {
+ return Status;
+ }
+
+ for (;;) {
+ StartBlock = LastStartBlock;
+ BufferSize = *BufferSizePtr - Offset;
+
+ if (DontUseBuffer) {
+ //
+ // overwrie the temp buf
+ //
+ BufferPtrLocal = BufferPtr;
+ } else {
+ BufferPtrLocal = BufferPtr + Offset;
+
+ }
+ //
+ // special !!! do not leave enabled in saved version on Source Safe
+ // Following code put in in order to create a special version for regression
+ // test of MTFTP server to make sure it handles mulitple opens correctly.
+ // This code should NOT be enabled normally.
+ //
+#ifdef SpecialNowaitVersion
+#pragma message ("This is special version for MTFTP regression test")
+ if (StartBlock || !LastBlock)
+#endif
+ if (((Status = MtftpListen (
+ Private,
+ &BufferSize,
+ BufferPtrLocal,
+ ServerIpPtr,
+ MtftpInfoPtr,
+ &StartBlock,
+ &NumMissed,
+ TransTimeout,
+ ListenTimeout,
+ LastBlock,
+ DontUseBuffer
+ )) != EFI_SUCCESS) && (Status != EFI_TIMEOUT)) {
+ return Status;
+ //
+ // failed
+ //
+ }
+ //
+ // if none were received, start block is not reset
+ //
+ if (StartBlock == LastStartBlock) {
+ UINT8 CompStat;
+
+ //
+ // timed out with none received - try MTFTP open
+ //
+ if ((Status = MtftpOpen (
+ Private,
+ BufferSizePtr,
+ BufferPtr,
+ &Offset,
+ ServerIpPtr,
+ FilenamePtr,
+ MtftpInfoPtr,
+ &CompStat,
+ DontUseBuffer
+ )) != EFI_SUCCESS) {
+ //
+ // open failure - try TFTP
+ //
+ return Status;
+ }
+ //
+ // return code EFI_SUCCESS
+ // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done
+ // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest
+ // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all
+ // (do not get = GOTUNI - returns NO_DATA go will go to TFTP session)
+ //
+ if (CompStat == (GOTUNI | GOTMULTI)) {
+ //
+ // finished - got it all
+ //
+ return Status;
+ }
+
+ if (CompStat) {
+ //
+ // offset is two packet lengths
+ //
+ Offset <<= 1;
+ //
+ // last block received
+ //
+ LastStartBlock = 2;
+ } else {
+ Offset = 0;
+ LastStartBlock = 0;
+ }
+
+ ListenTimeout = TransTimeout;
+ continue;
+ }
+ //
+ // did we get the last block
+ //
+ if (Status == EFI_SUCCESS) {
+ //
+ // yes - set the file size if this was first time
+ //
+ if (!LastBlock) {
+ *BufferSizePtr -= BufferSize;
+ }
+ //
+ // if buffer was too small, finished
+ //
+ if (!DontUseBuffer) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ //
+ // if we got them all, finished
+ //
+ if (!NumMissed && StartBlock == LastStartBlock + 1) {
+ return Status;
+ }
+ //
+ // did not get them all - set last block
+ //
+ LastBlock = (UINT16) (StartBlock - 1);
+ }
+ //
+ // compute listen timeout
+ //
+ ListenTimeout = (UINT16) ((NumMissed > MtftpInfoPtr->ListenTimeout) ? 0 : (MtftpInfoPtr->ListenTimeout - NumMissed));
+
+ //
+ // reset
+ //
+ Offset = 0;
+ LastStartBlock = 0;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+EFI_STATUS
+TftpInfo (
+ PXE_BASECODE_DEVICE *Private,
+ UINT64 *BufferSizePtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ EFI_PXE_BASE_CODE_UDP_PORT SrvPort,
+ UINT8 *FilenamePtr,
+ UINTN *PacketSizePtr
+ )
+{
+ EFI_PXE_BASE_CODE_UDP_PORT OurPort;
+ EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort;
+ EFI_STATUS Status;
+ UINT64 BlockNum;
+ UINTN Offset;
+ UINTN ReplyLen;
+ UINT8 *Ptr;
+
+ union {
+ struct Tftpv4Oack OAck2Ptr;
+ struct Tftpv4Ack Ack2Ptr;
+ struct Tftpv4Data Datastr;
+ } u;
+
+ OurPort = 0;
+ ServerReplyPort = 0;
+ ReplyLen = sizeof (u.Datastr.Data);
+
+ //
+ // send a write request with the blocksize option -
+ // sets our IP and port - and receive reply - sets his port
+ // will retry operation up to 3 times if no response,
+ // and will retry without options on an error reply
+ //
+ if ((Status = TftpRwReqwResp (
+ TFTP_RRQ,
+ /* BIGBLKNUMOP | */BKSZOP | TSIZEOP,
+ Private,
+ &u,
+ PacketSizePtr,
+ &ReplyLen,
+ u.Datastr.Data,
+ ServerIpPtr,
+ &SrvPort,
+ &ServerReplyPort,
+ &OurPort,
+ FilenamePtr,
+ REQ_RESP_TIMEOUT
+ )) != EFI_SUCCESS) {
+ DEBUG ((DEBUG_WARN, "\nTftpInfo() Exit #1"));
+ return Status;
+ }
+ //
+ // check for good OACK
+ //
+ if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {
+ //
+ // now parse it for options
+ // bigblk#
+ //
+ Ptr = FindOption (
+ BigBlkNumOp,
+ sizeof (BigBlkNumOp),
+ u.OAck2Ptr.OpAck[0].Option,
+ ReplyLen + sizeof (u.Ack2Ptr.BlockNum)
+ );
+
+ if (Ptr != NULL) {
+ if (AtoU (Ptr) == 8) {
+ Private->BigBlkNumFlag = TRUE;
+ } else {
+ return EFI_PROTOCOL_ERROR;
+ }
+ }
+ //
+ // blksize
+ //
+ Ptr = FindOption (
+ BlockSizeOp,
+ sizeof (BlockSizeOp),
+ u.OAck2Ptr.OpAck[0].Option,
+ ReplyLen += sizeof (u.Ack2Ptr.BlockNum)
+ );
+
+ *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512;
+
+ //
+ // tsize
+ //
+ Ptr = FindOption (
+ TsizeOp,
+ sizeof (TsizeOp),
+ u.OAck2Ptr.OpAck[0].Option,
+ ReplyLen
+ );
+
+ if (Ptr != NULL) {
+ *BufferSizePtr = AtoU64 (Ptr);
+
+ //
+ // teminate session with error
+ //
+ SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort);
+
+ return EFI_SUCCESS;
+ }
+
+ Offset = 0;
+ BlockNum = 0;
+ } else {
+ //
+ // if MTFTP get filesize, return unsupported
+ //
+ if (SrvPort != TftpRequestPort) {
+ SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort);
+ DEBUG ((DEBUG_WARN, "\nTftpInfo() Exit #3"));
+ return EFI_UNSUPPORTED;
+ }
+
+ Offset = ReplyLen;
+ //
+ // last block received
+ //
+ BlockNum = 1;
+ }
+ //
+ // does not support the option - do a download with no buffer
+ //
+ *BufferSizePtr = 0;
+
+ Status = LockStepReceive (
+ Private,
+ (UINT16) ReplyLen,
+ BufferSizePtr,
+ Offset,
+ (INT8 *) &u,
+ ServerIpPtr,
+ &ServerReplyPort,
+ &Private->EfiBc.Mode->StationIp,
+ &OurPort,
+ BlockNum,
+ ACK_TIMEOUT,
+ TRUE
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((DEBUG_WARN, "\nTftpInfo() LockStepReceive() == %Xh", Status));
+ }
+
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+EFI_STATUS
+TftpDownload (
+ PXE_BASECODE_DEVICE *Private,
+ UINT64 *BufferSizePtr,
+ UINT8 *BufferPtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ UINT8 *FilenamePtr,
+ UINTN *PacketSizePtr,
+ EFI_PXE_BASE_CODE_UDP_PORT SrvPort,
+ UINT16 Req,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_PXE_BASE_CODE_UDP_PORT OurPort;
+ EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort;
+ EFI_STATUS Status;
+ UINT64 Offset;
+ UINT64 BlockNum;
+ UINTN ReplyLen;
+ UINT8 *Ptr;
+
+ union {
+ struct Tftpv4Ack Ack2Ptr;
+ struct Tftpv4Oack OAck2Ptr;
+ struct Tftpv4Data Data;
+ struct Tftpv4Ack8 Ack8Ptr;
+ struct Tftpv4Data8 Data8;
+ } U;
+
+ OurPort = 0;
+ ServerReplyPort = 0;
+ ReplyLen = (UINTN) ((*BufferSizePtr > 0xFFFF) ? 0xFFFF : *BufferSizePtr);
+
+ //
+ // send a read request with the blocksize option - sets our IP and port
+ // - and receive reply - sets his port will retry operation up to 3
+ // times if no response, and will retry without options on an error
+ // reply
+ //
+ if ((Status = TftpRwReqwResp (
+ Req,
+ /* BIGBLKNUMOP | */BKSZOP,
+ Private,
+ &U,
+ PacketSizePtr,
+ &ReplyLen,
+ BufferPtr,
+ ServerIpPtr,
+ &SrvPort,
+ &ServerReplyPort,
+ &OurPort,
+ FilenamePtr,
+ REQ_RESP_TIMEOUT
+ )) != EFI_SUCCESS) {
+ DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #1 %xh (%r)", Status, Status));
+ return Status;
+ }
+ //
+ // check for OACK
+ //
+ if (U.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {
+ //
+ // get the OACK
+ //
+ CopyMem (U.Data.Data, BufferPtr, ReplyLen);
+
+ Ptr = FindOption (
+ BigBlkNumOp,
+ sizeof (BigBlkNumOp),
+ U.OAck2Ptr.OpAck[0].Option,
+ ReplyLen + sizeof (U.Ack2Ptr.BlockNum)
+ );
+
+ if (Ptr != NULL) {
+ if (AtoU (Ptr) == 8) {
+ Private->BigBlkNumFlag = TRUE;
+ } else {
+ return EFI_PROTOCOL_ERROR;
+ }
+ }
+ //
+ // now parse it for blocksize option
+ //
+ Ptr = FindOption (
+ BlockSizeOp,
+ sizeof (BlockSizeOp),
+ U.OAck2Ptr.OpAck[0].Option,
+ ReplyLen += sizeof (U.Ack2Ptr.BlockNum)
+ );
+
+ ReplyLen = (Ptr != NULL) ? AtoU (Ptr) : 512;
+
+ Offset = 0;
+ //
+ // last block received
+ //
+ BlockNum = 0;
+ } else if (U.Ack2Ptr.OpCode != HTONS (TFTP_DATA) || U.Ack2Ptr.BlockNum != HTONS (1)) {
+ //
+ // or data
+ //
+ DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #2 %xh (%r)", Status, Status));
+
+ return EFI_PROTOCOL_ERROR;
+ } else {
+ //
+ // got good data packet
+ //
+ Offset = ReplyLen;
+ //
+ // last block received
+ //
+ BlockNum = 1;
+ }
+
+ if (PacketSizePtr != NULL) {
+ *PacketSizePtr = ReplyLen;
+ }
+ //
+ // routine to read rest of file after a successful open (TFTP or MTFTP)
+ // sends ACK and gets next data packet until short packet arrives, then sends
+ // ACK and (hopefully) times out
+ // if first packet has been read, BufferPtr and BufferSize must reflect fact
+ //
+ Status = LockStepReceive (
+ Private,
+ ReplyLen,
+ BufferSizePtr,
+ Offset,
+ BufferPtr,
+ ServerIpPtr,
+ &ServerReplyPort,
+ &Private->EfiBc.Mode->StationIp,
+ &OurPort,
+ BlockNum,
+ ACK_TIMEOUT,
+ DontUseBuffer
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #3 %xh (%r)", Status, Status));
+
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Status = TftpInfo (
+ Private,
+ BufferSizePtr,
+ ServerIpPtr,
+ SrvPort,
+ FilenamePtr,
+ PacketSizePtr
+ );
+
+ if (!EFI_ERROR (Status)) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ }
+ }
+ }
+
+ return Status;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+STATIC
+EFI_STATUS
+TftpUpload (
+ PXE_BASECODE_DEVICE *Private,
+ UINT64 *BufferSizePtr,
+ VOID *BufferPtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ UINT8 *FilenamePtr,
+ UINTN *PacketSizePtr,
+ BOOLEAN Overwrite
+ )
+{
+ struct Tftpv4Ack Header;
+ EFI_PXE_BASE_CODE_UDP_PORT OurPort;
+ EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort;
+ EFI_STATUS Status;
+ UINT64 BlockNum;
+ UINT64 TransferSize;
+ UINTN ReplyLen;
+ UINTN TransferLen;
+ UINT16 Options;
+ UINT8 *Ptr;
+
+ union {
+ struct Tftpv4Oack OAck2Ptr;
+ struct Tftpv4Ack Ack2Ptr;
+ struct Tftpv4Data Datastr;
+ } u;
+
+ OurPort = 0;
+ ServerReplyPort = 0;
+ TransferSize = *BufferSizePtr;
+ ReplyLen = sizeof (u.Datastr.Data);
+ Options = (UINT16) ((Overwrite) ? OVERWRITEOP | BKSZOP : BKSZOP);
+
+ //
+ // send a write request with the blocksize option - sets our IP and port -
+ // and receive reply - sets his port
+ // will retry operation up to 3 times if no response, and will retry without
+ // options on an error reply
+ //
+ if ((Status = TftpRwReqwResp (
+ TFTP_WRQ,
+ Options,
+ Private,
+ &u,
+ PacketSizePtr,
+ &ReplyLen,
+ u.Datastr.Data,
+ ServerIpPtr,
+ &TftpRequestPort,
+ &ServerReplyPort,
+ &OurPort,
+ FilenamePtr,
+ REQ_RESP_TIMEOUT
+ )) != EFI_SUCCESS) {
+ return Status;
+ }
+ //
+ // check for OACK
+ //
+ if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {
+ //
+ // parse it for blocksize option
+ //
+ Ptr = FindOption (
+ BlockSizeOp,
+ sizeof (BlockSizeOp),
+ u.OAck2Ptr.OpAck[0].Option,
+ ReplyLen += sizeof (u.Ack2Ptr.BlockNum)
+ );
+ *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512;
+ }
+ //
+ // or ACK
+ //
+ else if (u.Ack2Ptr.OpCode == HTONS (TFTP_ACK)) {
+ //
+ // option was not supported
+ //
+ *PacketSizePtr = 512;
+ } else {
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // loop
+ //
+ Header.OpCode = HTONS (TFTP_DATA);
+ BlockNum = 1;
+ Header.BlockNum = HTONS (1);
+
+ do {
+ UINTN HeaderSize;
+ INTN Retries;
+
+ Retries = NUM_ACK_RETRIES;
+ HeaderSize = sizeof (Header);
+ TransferLen = (UINTN) (MIN (*PacketSizePtr, TransferSize));
+
+ //
+ // write a data packet and get an ack
+ //
+ do {
+ //
+ // write
+ //
+ if ((Status = UdpWrite (
+ Private,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
+ ServerIpPtr,
+ &ServerReplyPort,
+ 0,
+ 0,
+ &OurPort,
+ &HeaderSize,
+ &Header,
+ &TransferLen,
+ BufferPtr
+ )) != EFI_SUCCESS) {
+ return Status;
+ }
+ //
+ // read reply
+ //
+ ReplyLen = sizeof (u.Datastr.Data);
+
+ if ((Status = TftpUdpRead (
+ Private,
+ 0,
+ &u,
+ &ReplyLen,
+ u.Datastr.Data,
+ ServerIpPtr,
+ &ServerReplyPort,
+ 0,
+ &OurPort,
+ ACK_TIMEOUT
+ )) == EFI_SUCCESS) {
+ //
+ // check for ACK for this data packet
+ //
+ if (u.Ack2Ptr.OpCode != HTONS (TFTP_ACK)) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if (u.Ack2Ptr.BlockNum != Header.BlockNum) {
+ //
+ // not for this packet - continue
+ //
+ Status = EFI_TIMEOUT;
+ }
+ }
+ } while (Status == EFI_TIMEOUT && --Retries);
+
+ if (Status != EFI_SUCCESS) {
+ return Status;
+ }
+
+ BufferPtr = (VOID *) ((UINT8 *) (BufferPtr) + TransferLen);
+ TransferSize -= TransferLen;
+ ++BlockNum;
+ Header.BlockNum = HTONS ((UINT16) BlockNum);
+ } while (TransferLen == *PacketSizePtr);
+
+ return EFI_SUCCESS;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return * EFI_INVALID_PARAMETER
+ @return * EFI_OUT_OF_RESOURCES
+ @return * EFI_BAD_BUFFER_SIZE
+ @return * Status is also returned from IpFilter(), TftpInfo(), MtftpDownload(),
+ @return * TftpDownload() and TftpUpload().
+
+**/
+EFI_STATUS
+PxeBcMtftp (
+ PXE_BASECODE_DEVICE *Private,
+ IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,
+ UINT64 *BufferSizePtr,
+ VOID *BufferPtr,
+ EFI_IP_ADDRESS *ServerIpPtr,
+ UINT8 *FilenamePtr,
+ UINTN *PacketSizePtr,
+ IN EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr, OPTIONAL
+ IN BOOLEAN Overwrite,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_PXE_BASE_CODE_IP_FILTER Filter;
+ EFI_STATUS StatCode;
+ EFI_STATUS Status;
+ UINT64 BufferSizeLocal;
+ UINTN PacketSize;
+ UINT8 *BufferPtrLocal;
+
+ Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ Filter.IpCnt = 0;
+ Filter.reserved = 0;
+
+ /* No error has occurred, yet. */
+ Private->EfiBc.Mode->TftpErrorReceived = FALSE;
+
+ /* We must at least have an MTFTP server IP address and
+ * a pointer to the buffer size.
+ */
+ if (!ServerIpPtr || !BufferSizePtr) {
+ DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #1"));
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_MTFTP;
+
+ //
+ // make sure filter set to unicast at start
+ //
+ if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {
+ DEBUG (
+ (DEBUG_NET,
+ "\nPxeBcMtftp() Exit IpFilter() == %Xh",
+ StatCode)
+ );
+
+ return StatCode;
+ }
+ //
+ // set unset parms to default values
+ //
+ if (!PacketSizePtr) {
+ *(PacketSizePtr = &PacketSize) = MAX_TFTP_PKT_SIZE;
+ }
+
+ if (*PacketSizePtr > *BufferSizePtr) {
+ *PacketSizePtr = (UINTN) *BufferSizePtr;
+ }
+
+ if (*PacketSizePtr < MIN_TFTP_PKT_SIZE) {
+ *PacketSizePtr = MIN_TFTP_PKT_SIZE;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*PacketSizePtr > BUFFER_ALLOCATE_SIZE) {
+ *PacketSizePtr = BUFFER_ALLOCATE_SIZE;
+ }
+
+ if (*PacketSizePtr > MAX_TFTP_PKT_SIZE) {
+ *PacketSizePtr = MAX_TFTP_PKT_SIZE;
+ }
+
+ if (Operation == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {
+ StatCode = TftpInfo (
+ Private,
+ BufferSizePtr,
+ ServerIpPtr,
+ TftpRequestPort,
+ FilenamePtr,
+ PacketSizePtr
+ );
+
+ if (StatCode != EFI_SUCCESS) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxeBcMtftp() Exit TftpInfo() == %Xh",
+ StatCode)
+ );
+ }
+
+ return StatCode;
+ }
+
+ if (Operation == EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE) {
+ if (!MtftpInfoPtr || !MtftpInfoPtr->SPort) {
+ DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #2"));
+ return EFI_INVALID_PARAMETER;
+ } else {
+ StatCode = TftpInfo (
+ Private,
+ BufferSizePtr,
+ ServerIpPtr,
+ MtftpInfoPtr->SPort,
+ FilenamePtr,
+ PacketSizePtr
+ );
+
+ gBS->Stall (10000);
+
+ if (StatCode != EFI_SUCCESS) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxeBcMtftp() Exit TftpInfo() == %Xh",
+ StatCode)
+ );
+ }
+
+ return StatCode;
+ }
+ }
+
+ if (!BufferPtr && !DontUseBuffer) {
+ //
+ // if dontusebuffer is false and no buffer???
+ //
+ DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #3"));
+ //
+ // DontUseBuffer can be true only for read_file operation
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DontUseBuffer) {
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ BUFFER_ALLOCATE_SIZE,
+ &BufferPtrLocal
+ );
+
+ if (EFI_ERROR (Status) || BufferPtrLocal == NULL) {
+ DEBUG ((DEBUG_NET, "\nPxeBcMtftp() Exit #4"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ BufferSizeLocal = BUFFER_ALLOCATE_SIZE;
+ } else {
+ if (!*BufferSizePtr && Operation != EFI_PXE_BASE_CODE_TFTP_WRITE_FILE) {
+ DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #5"));
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ BufferPtrLocal = BufferPtr;
+ BufferSizeLocal = *BufferSizePtr;
+ }
+
+ switch (Operation) {
+ case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:
+ if (FilenamePtr == NULL ||
+ MtftpInfoPtr == NULL ||
+ MtftpInfoPtr->MCastIp.Addr[0] == 0 ||
+ MtftpInfoPtr->SPort == 0 ||
+ MtftpInfoPtr->CPort == 0 ||
+ MtftpInfoPtr->ListenTimeout == 0 ||
+ MtftpInfoPtr->TransmitTimeout == 0
+ ) {
+ StatCode = EFI_INVALID_PARAMETER;
+ break;
+ }
+ //
+ // try MTFTP - if fails, drop into TFTP read
+ //
+ if ((StatCode = MtftpDownload (
+ Private,
+ &BufferSizeLocal,
+ BufferPtrLocal,
+ ServerIpPtr,
+ FilenamePtr,
+ MtftpInfoPtr,
+ DontUseBuffer
+ )) == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {
+ if (BufferSizePtr /* %% !DontUseBuffer */ ) {
+ *BufferSizePtr = BufferSizeLocal;
+ }
+
+ break;
+ }
+ //
+ // go back to unicast
+ //
+ if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {
+ break;
+ }
+
+ /* fall thru */
+ case EFI_PXE_BASE_CODE_TFTP_READ_FILE:
+ if (FilenamePtr == NULL) {
+ StatCode = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ StatCode = TftpDownload (
+ Private,
+ &BufferSizeLocal,
+ BufferPtrLocal,
+ ServerIpPtr,
+ FilenamePtr,
+ PacketSizePtr,
+ TftpRequestPort,
+ TFTP_RRQ,
+ DontUseBuffer
+ );
+
+ if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {
+ if (BufferSizePtr /* !DontUseBuffer */ ) {
+ *BufferSizePtr = BufferSizeLocal;
+ }
+ }
+
+ break;
+
+ case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:
+ if (FilenamePtr == NULL || DontUseBuffer) {
+ //
+ // not a valid option
+ //
+ StatCode = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ StatCode = TftpUpload (
+ Private,
+ BufferSizePtr,
+ BufferPtr,
+ ServerIpPtr,
+ FilenamePtr,
+ PacketSizePtr,
+ Overwrite
+ );
+
+ if (StatCode != EFI_SUCCESS) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxeBcMtftp() Exit #6 %xh (%r)",
+ StatCode,
+ StatCode)
+ );
+ }
+
+ return StatCode;
+
+ case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:
+ if (FilenamePtr == NULL || DontUseBuffer) {
+ //
+ // not a valid option
+ //
+ StatCode = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ StatCode = TftpDownload (
+ Private,
+ BufferSizePtr,
+ BufferPtr,
+ ServerIpPtr,
+ FilenamePtr,
+ PacketSizePtr,
+ TftpRequestPort,
+ TFTP_DIR,
+ DontUseBuffer
+ );
+
+ if (StatCode != EFI_SUCCESS) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxeBcMtftp() Exit #7 %xh (%r)",
+ StatCode,
+ StatCode)
+ );
+ }
+
+ return StatCode;
+
+ case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:
+ if (DontUseBuffer) {
+ StatCode = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (MtftpInfoPtr == NULL || !MtftpInfoPtr->SPort) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxeBcMtftp() Exit #9 %xh (%r)",
+ EFI_INVALID_PARAMETER,
+ EFI_INVALID_PARAMETER)
+ );
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ StatCode = TftpDownload (
+ Private,
+ BufferSizePtr,
+ BufferPtr,
+ ServerIpPtr,
+ (UINT8 *) "/",
+ PacketSizePtr,
+ MtftpInfoPtr->SPort,
+ TFTP_DIR,
+ DontUseBuffer
+ );
+
+ break;
+
+ default:
+ StatCode = EFI_INVALID_PARAMETER;
+ }
+
+ if (DontUseBuffer) {
+ gBS->FreePool (BufferPtrLocal);
+ }
+
+ if (StatCode != EFI_SUCCESS) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nPxeBcMtftp() Exit #8 %xh (%r)",
+ StatCode,
+ StatCode)
+ );
+ }
+
+ gBS->Stall (10000);
+
+ return StatCode;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return * EFI_INVALID_PARAMETER
+ @return * Status is also returned from PxeBcMtftp();
+
+**/
+EFI_STATUS
+EFIAPI
+BcMtftp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL * This,
+ IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,
+ IN OUT VOID *BufferPtr,
+ IN BOOLEAN Overwrite,
+ IN OUT UINT64 *BufferSizePtr,
+ IN UINTN *BlockSizePtr OPTIONAL,
+ IN EFI_IP_ADDRESS * ServerIpPtr,
+ IN UINT8 *FilenamePtr,
+ IN EFI_PXE_BASE_CODE_MTFTP_INFO * MtftpInfoPtr OPTIONAL,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_PXE_BASE_CODE_IP_FILTER Filter;
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_INADDR_UNICAST (ServerIpPtr)) {
+ //
+ // The station IP is not a unicast address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+ //
+ // Issue BC command
+ //
+ Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ Filter.IpCnt = 0;
+ Filter.reserved = 0;
+
+ DEBUG ((DEBUG_WARN, "\nBcMtftp() Op=%d Buf=%Xh", Operation, BufferPtr));
+
+ StatCode = PxeBcMtftp (
+ Private,
+ Operation,
+ BufferSizePtr,
+ BufferPtr,
+ ServerIpPtr,
+ FilenamePtr,
+ BlockSizePtr,
+ MtftpInfoPtr,
+ Overwrite,
+ DontUseBuffer
+ );
+
+ //
+ // restore to unicast
+ //
+ IpFilter (Private, &Filter);
+
+ //
+ // Unlock the instance data
+ //
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+
+/* eof - PxeBcMtftp.c */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c
new file mode 100644
index 0000000000..fcb55acfa0
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c
@@ -0,0 +1,517 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ pxe_bc_udp.c
+
+Abstract:
+
+
+**/
+
+#include "Bc.h"
+
+//
+// //////////////////////////////////////////////////////////////////////
+//
+// Udp Write Routine - called by base code - e.g. TFTP - already locked
+//
+
+/**
+
+ @return EFI_SUCCESS :=
+ @return EFI_INVALID_PARAMETER :=
+ @return other :=
+
+**/
+EFI_STATUS
+UdpWrite (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN UINT16 OpFlags,
+ IN EFI_IP_ADDRESS *DestIpPtr,
+ IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr,
+ IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL
+ IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL
+ IN UINTN *HeaderSizePtr, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN UINTN *BufferSizeptr,
+ IN VOID *BufferPtr
+ )
+{
+ UINTN TotalLength;
+ UINTN HeaderSize;
+ EFI_PXE_BASE_CODE_UDP_PORT DefaultSrcPort;
+
+ //
+ //
+ //
+ HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0;
+ DefaultSrcPort = 0;
+
+ //
+ // check parameters
+ //
+ if (BufferSizeptr == NULL ||
+ BufferPtr == NULL ||
+ DestIpPtr == NULL ||
+ DestPortPtr == NULL ||
+ (HeaderSizePtr != NULL && *HeaderSizePtr == 0) ||
+ (HeaderSize != 0 && HeaderPtr == NULL) ||
+ (GatewayIpPtr != NULL && !IS_INADDR_UNICAST(GatewayIpPtr)) ||
+ (OpFlags &~(EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT))
+ ) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nUdpWrite() Exit #1 %xh (%r)",
+ EFI_INVALID_PARAMETER,
+ EFI_INVALID_PARAMETER)
+ );
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TotalLength = *BufferSizeptr + HeaderSize + sizeof (UDPV4_HEADER);
+
+ if (TotalLength > 0x0000ffff) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nUdpWrite() Exit #2 %xh (%r)",
+ EFI_BAD_BUFFER_SIZE,
+ EFI_BAD_BUFFER_SIZE)
+ );
+
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (SrcIpPtr == NULL) {
+ SrcIpPtr = &Private->EfiBc.Mode->StationIp;
+ }
+
+ if (SrcPortPtr == NULL) {
+ SrcPortPtr = &DefaultSrcPort;
+ OpFlags |= EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT;
+ }
+
+ if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) {
+ *SrcPortPtr = Private->RandomPort;
+
+ if (++Private->RandomPort == 0) {
+ Private->RandomPort = PXE_RND_PORT_LOW;
+ }
+ }
+
+#define IpTxBuffer ((IPV4_BUFFER *) Private->TransmitBufferPtr)
+ //
+ // build pseudo header and udp header in transmit buffer
+ //
+#define Udpv4Base ((UDPV4_HEADERS *) (IpTxBuffer->u.Data - sizeof (UDPV4_PSEUDO_HEADER)))
+
+ Udpv4Base->Udpv4PseudoHeader.SrcAddr.L = SrcIpPtr->Addr[0];
+ Udpv4Base->Udpv4PseudoHeader.DestAddr.L = DestIpPtr->Addr[0];
+ Udpv4Base->Udpv4PseudoHeader.Zero = 0;
+ Udpv4Base->Udpv4PseudoHeader.Protocol = PROT_UDP;
+ Udpv4Base->Udpv4PseudoHeader.TotalLength = HTONS (TotalLength);
+ Udpv4Base->Udpv4Header.SrcPort = HTONS (*SrcPortPtr);
+ Udpv4Base->Udpv4Header.DestPort = HTONS (*DestPortPtr);
+ Udpv4Base->Udpv4Header.TotalLength = Udpv4Base->Udpv4PseudoHeader.TotalLength;
+ Udpv4Base->Udpv4Header.Checksum = 0;
+
+ if (HeaderSize != 0) {
+ CopyMem (IpTxBuffer->u.Udp.Data, HeaderPtr, HeaderSize);
+ }
+
+ HeaderSize += sizeof (UDPV4_HEADER);
+
+ Udpv4Base->Udpv4Header.Checksum = IpChecksum2 (
+ (UINT16 *) Udpv4Base,
+ HeaderSize + sizeof (UDPV4_PSEUDO_HEADER),
+ (UINT16 *) BufferPtr,
+ (UINT16) *BufferSizeptr
+ );
+
+ if (Udpv4Base->Udpv4Header.Checksum == 0) {
+ Udpv4Base->Udpv4Header.Checksum = 0xffff;
+ //
+ // transmit zero checksum as ones complement
+ //
+ }
+
+ return Ip4Send (
+ Private,
+ OpFlags,
+ PROT_UDP,
+ Udpv4Base->Udpv4PseudoHeader.SrcAddr.L,
+ Udpv4Base->Udpv4PseudoHeader.DestAddr.L,
+ (GatewayIpPtr) ? GatewayIpPtr->Addr[0] : 0,
+ HeaderSize,
+ BufferPtr,
+ *BufferSizeptr
+ );
+}
+//
+// //////////////////////////////////////////////////////////
+//
+// BC Udp Write Routine
+//
+
+/**
+
+ @return EFI_SUCCESS :=
+ @return other :=
+
+**/
+EFI_STATUS
+EFIAPI
+BcUdpWrite (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN EFI_IP_ADDRESS *DestIpPtr,
+ IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr,
+ IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL
+ IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL
+ IN UINTN *HeaderSizePtr, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN UINTN *BufferSizeptr,
+ IN VOID *BufferPtr
+ )
+{
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_WRITE;
+
+ //
+ // Issue BC command
+ //
+ StatCode = UdpWrite (
+ Private,
+ OpFlags,
+ DestIpPtr,
+ DestPortPtr,
+ GatewayIpPtr,
+ SrcIpPtr,
+ SrcPortPtr,
+ HeaderSizePtr,
+ HeaderPtr,
+ BufferSizeptr,
+ BufferPtr
+ );
+
+ //
+ // Unlock the instance data
+ //
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+//
+// /////////////////////////////////////////////////////////////////////
+//
+// Udp Read Routine - called by base code - e.g. TFTP - already locked
+//
+
+/**
+
+ @return EFI_SUCCESS :=
+ @return EFI_INVALID_PARAMETER :=
+ @return other :=
+
+**/
+EFI_STATUS
+UdpRead (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIpPtr, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, OPTIONAL
+ IN OUT EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL
+ IN UINTN *HeaderSizePtr, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN OUT UINTN *BufferSizeptr,
+ IN VOID *BufferPtr,
+ EFI_EVENT TimeoutEvent
+ )
+{
+ EFI_STATUS StatCode;
+ EFI_IP_ADDRESS TmpSrcIp;
+ EFI_IP_ADDRESS TmpDestIp;
+ UINTN BufferSize;
+ UINTN HeaderSize;
+
+ //
+ // combination structure of pseudo header/udp header
+ //
+#pragma pack (1)
+ struct {
+ UDPV4_PSEUDO_HEADER Udpv4PseudoHeader;
+ UDPV4_HEADER Udpv4Header;
+ UINT8 ProtHdr[64];
+ } Hdrs;
+#pragma pack ()
+
+ HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0;
+ //
+ // read [with filtering]
+ // check parameters
+ //
+ if (BufferSizeptr == NULL ||
+ BufferPtr == NULL ||
+ (HeaderSize != 0 && HeaderPtr == NULL) ||
+ (OpFlags &~UDP_FILTER_MASK)
+ //
+ // if filtering on a particular IP/Port, need it
+ //
+ ||
+ (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr == NULL) ||
+ (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && SrcPortPtr == NULL) ||
+ (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && DestPortPtr == NULL)
+ ) {
+ DEBUG ((DEBUG_INFO, "\nUdpRead() Exit #1 Invalid Parameter"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // in case we loop
+ //
+ BufferSize = *BufferSizeptr;
+ //
+ // we need source and dest IPs for pseudo header
+ //
+ if (SrcIpPtr == NULL) {
+ SrcIpPtr = &TmpSrcIp;
+ }
+
+ if (DestIpPtr == NULL) {
+ DestIpPtr = &TmpDestIp;
+ TmpDestIp = Private->EfiBc.Mode->StationIp;
+ }
+
+#if SUPPORT_IPV6
+ if (Private->EfiBc.Mode->UsingIpv6) {
+ //
+ // %%TBD
+ //
+ }
+#endif
+
+ for (;;) {
+ *BufferSizeptr = BufferSize;
+
+ StatCode = IpReceive (
+ Private,
+ OpFlags,
+ SrcIpPtr,
+ DestIpPtr,
+ PROT_UDP,
+ &Hdrs.Udpv4Header,
+ HeaderSize + sizeof Hdrs.Udpv4Header,
+ BufferPtr,
+ BufferSizeptr,
+ TimeoutEvent
+ );
+
+ if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {
+ UINT16 SPort;
+ UINT16 DPort;
+
+ SPort = NTOHS (Hdrs.Udpv4Header.SrcPort);
+ DPort = NTOHS (Hdrs.Udpv4Header.DestPort);
+
+ //
+ // do filtering
+ //
+ if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && *SrcPortPtr != SPort) {
+ continue;
+ }
+
+ if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && *DestPortPtr != DPort) {
+ continue;
+ }
+ //
+ // check checksum
+ //
+ if (StatCode == EFI_SUCCESS && Hdrs.Udpv4Header.Checksum) {
+ Hdrs.Udpv4PseudoHeader.SrcAddr.L = SrcIpPtr->Addr[0];
+ Hdrs.Udpv4PseudoHeader.DestAddr.L = DestIpPtr->Addr[0];
+ Hdrs.Udpv4PseudoHeader.Zero = 0;
+ Hdrs.Udpv4PseudoHeader.Protocol = PROT_UDP;
+ Hdrs.Udpv4PseudoHeader.TotalLength = Hdrs.Udpv4Header.TotalLength;
+
+ if (Hdrs.Udpv4Header.Checksum == 0xffff) {
+ Hdrs.Udpv4Header.Checksum = 0;
+ }
+
+ if (IpChecksum2 (
+ (UINT16 *) &Hdrs.Udpv4PseudoHeader,
+ HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader) + sizeof (Hdrs.Udpv4Header),
+ (UINT16 *) BufferPtr,
+ *BufferSizeptr
+ )) {
+ DEBUG (
+ (DEBUG_INFO,
+ "\nUdpRead() Hdrs.Udpv4PseudoHeader == %Xh",
+ Hdrs.Udpv4PseudoHeader)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nUdpRead() Header size == %d",
+ HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader))
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nUdpRead() BufferPtr == %Xh",
+ BufferPtr)
+ );
+ DEBUG (
+ (DEBUG_INFO,
+ "\nUdpRead() Buffer size == %d",
+ *BufferSizeptr)
+ );
+ DEBUG ((DEBUG_INFO, "\nUdpRead() Exit #2 Device Error"));
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ //
+ // all passed
+ //
+ if (SrcPortPtr != NULL) {
+ *SrcPortPtr = SPort;
+ }
+
+ if (DestPortPtr != NULL) {
+ *DestPortPtr = DPort;
+ }
+
+ if (HeaderSize != 0) {
+ CopyMem (HeaderPtr, Hdrs.ProtHdr, HeaderSize);
+ }
+ }
+
+ if ((StatCode != EFI_SUCCESS) && (StatCode != EFI_TIMEOUT)) {
+ DEBUG (
+ (DEBUG_INFO,
+ "\nUdpRead() Exit #3 %Xh %r",
+ StatCode,
+ StatCode)
+ );
+ }
+
+ return StatCode;
+ }
+}
+//
+// //////////////////////////////////////////////////////////
+//
+// BC Udp Read Routine
+//
+
+/**
+
+ @return EFI_SUCCESS :=
+ @return other :=
+
+**/
+EFI_STATUS
+EFIAPI
+BcUdpRead (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL
+ IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN OUT UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ EFI_STATUS StatCode;
+ PXE_BASECODE_DEVICE *Private;
+
+ //
+ // Lock the instance data and make sure started
+ //
+ StatCode = EFI_SUCCESS;
+
+ if (This == NULL) {
+ DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiAcquireLock (&Private->Lock);
+
+ if (This->Mode == NULL || !This->Mode->Started) {
+ DEBUG ((DEBUG_ERROR, "BC was not started."));
+ EfiReleaseLock (&Private->Lock);
+ return EFI_NOT_STARTED;
+ }
+
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_READ;
+
+ //
+ // Issue BC command
+ //
+ StatCode = UdpRead (
+ Private,
+ OpFlags,
+ DestIp,
+ DestPort,
+ SrcIp,
+ SrcPort,
+ HeaderSize,
+ HeaderPtr,
+ BufferSize,
+ BufferPtr,
+ 0
+ );
+
+ //
+ // Unlock the instance data and return
+ //
+ EfiReleaseLock (&Private->Lock);
+ return StatCode;
+}
+
+/* eof - pxe_bc_udp.c */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c
new file mode 100644
index 0000000000..7c5d0bc2fb
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c
@@ -0,0 +1,1614 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ pxe_loadfile.c
+
+Abstract:
+ An implementation of the load file protocol for network devices.
+
+
+**/
+
+
+#include "Bc.h"
+
+#define DO_MENU (EFI_SUCCESS)
+#define NO_MENU (DO_MENU + 1)
+#define LOCAL_BOOT (EFI_ABORTED)
+#define AUTO_SELECT (NO_MENU)
+
+#define NUMBER_ROWS 25 // we set to mode 0
+#define MAX_MENULIST 23
+
+#define Ctl(x) (0x1F & (x))
+
+typedef union {
+ DHCPV4_OP_STRUCT *OpPtr;
+ PXE_BOOT_MENU_ENTRY *CurrentMenuItemPtr;
+ PXE_OP_DISCOVERY_CONTROL *DiscCtlOpStr;
+ PXE_OP_BOOT_MENU *MenuPtr;
+ UINT8 *BytePtr;
+} UNION_PTR;
+
+
+
+/**
+ PxeBc callback routine for status updates and aborts.
+
+ @param This Pointer to PxeBcCallback
+ interface
+ @param Function PxeBc function ID#
+ @param Received Receive/transmit flag
+ @param PacketLength Length of received packet (0
+ == idle callback)
+ @param PacketPtr Pointer to received packet
+ (NULL == idle callback)
+
+ @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
+ EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
+ -
+
+**/
+STATIC
+EFI_PXE_BASE_CODE_CALLBACK_STATUS
+EFIAPI
+bc_callback (
+ IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL * This,
+ IN EFI_PXE_BASE_CODE_FUNCTION Function,
+ IN BOOLEAN Received,
+ IN UINT32 PacketLength,
+ IN EFI_PXE_BASE_CODE_PACKET * PacketPtr OPTIONAL
+ )
+{
+ STATIC UINTN Propeller;
+
+ EFI_INPUT_KEY Key;
+ UINTN Row;
+ UINTN Col;
+
+ Propeller = 0;
+ //
+ // Resolve Warning 4 unreferenced parameter problem
+ //
+ This = This;
+
+ //
+ // Check for user abort.
+ //
+ if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_SUCCESS) {
+ if (!Key.ScanCode) {
+ if (Key.UnicodeChar == Ctl ('c')) {
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
+ }
+ } else if (Key.ScanCode == SCAN_ESC) {
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
+ }
+ }
+ //
+ // Do nothing if this is a receive.
+ //
+ if (Received) {
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+ //
+ // The display code is only for these functions.
+ //
+ switch (Function) {
+ case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:
+ //
+ // If this is a transmit and not a M/TFTP open request,
+ // return now. Do not print a dot for each M/TFTP packet
+ // that is sent, only for the open packets.
+ //
+ if (PacketLength != 0 && PacketPtr != NULL) {
+ if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+ }
+
+ break;
+
+ case EFI_PXE_BASE_CODE_FUNCTION_DHCP:
+ case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:
+ break;
+
+ default:
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+ //
+ // Display routines
+ //
+ if (PacketLength != 0 && PacketPtr != NULL) {
+ //
+ // Display a '.' when a packet is transmitted.
+ //
+ AsciiPrint (".");
+ } else if (PacketLength == 0 && PacketPtr == NULL) {
+ //
+ // Display a propeller when waiting for packets if at
+ // least 200 ms have passed.
+ //
+ Row = gST->ConOut->Mode->CursorRow;
+ Col = gST->ConOut->Mode->CursorColumn;
+
+ AsciiPrint ("%c", "/-\\|"[Propeller]);
+ gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row);
+
+ Propeller = (Propeller + 1) & 3;
+ }
+
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+}
+
+STATIC EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL _bc_callback = {
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,
+ &bc_callback
+};
+
+
+/**
+ Display an IPv4 address in dot notation.
+
+ @param Ptr Pointer to IPv4 address.
+
+ @return None
+
+**/
+STATIC
+VOID
+PrintIpv4 (
+ UINT8 *Ptr
+ )
+{
+ if (Ptr != NULL) {
+ AsciiPrint ("%d.%d.%d.%d", Ptr[0], Ptr[1], Ptr[2], Ptr[3]);
+ }
+}
+
+
+/**
+ Display client and server IP information.
+
+ @param Private Pointer to PxeBc interface
+
+ @return None
+
+**/
+STATIC
+VOID
+ShowMyInfo (
+ IN PXE_BASECODE_DEVICE *Private
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ UINTN Index;
+
+ //
+ // Do nothing if a NULL pointer is passed in.
+ //
+ if (Private == NULL) {
+ return ;
+ }
+ //
+ // Get pointer to PXE BaseCode mode structure
+ //
+ PxeBcMode = Private->EfiBc.Mode;
+
+ //
+ // Display client IP address
+ //
+ AsciiPrint ("\rCLIENT IP: ");
+ PrintIpv4 (PxeBcMode->StationIp.v4.Addr);
+
+ //
+ // Display subnet mask
+ //
+ AsciiPrint (" MASK: ");
+ PrintIpv4 (PxeBcMode->SubnetMask.v4.Addr);
+
+ //
+ // Display DHCP and proxyDHCP IP addresses
+ //
+ if (PxeBcMode->ProxyOfferReceived) {
+ AsciiPrint ("\nDHCP IP: ");
+ PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
+
+ AsciiPrint (" PROXY IP: ");
+ PrintIpv4 (((DHCPV4_OP_SERVER_IP *) PXE_OFFER_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
+ } else {
+ AsciiPrint (" DHCP IP: ");
+ PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
+ }
+ //
+ // Display gateway IP addresses
+ //
+ for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {
+ if ((Index % 3) == 0) {
+ AsciiPrint ("\r\nGATEWAY IP:");
+ }
+
+ AsciiPrint (" ");
+ PrintIpv4 (PxeBcMode->RouteTable[Index].GwAddr.v4.Addr);
+ AsciiPrint (" ");
+ }
+
+ AsciiPrint ("\n");
+}
+
+
+/**
+ Display prompt and wait for input.
+
+ @param Private Pointer to PxeBc interface
+ @param BootPromptPtr Pointer to PXE boot prompt
+ option
+
+ @retval AUTO_SELECT DO_MENU -
+ @retval NO_MENU
+ @retval LOCAL_BOOT
+
+**/
+STATIC
+EFI_STATUS
+DoPrompt (
+ PXE_BASECODE_DEVICE *Private,
+ PXE_OP_BOOT_PROMPT *BootPromptPtr
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT TimeoutEvent;
+ EFI_EVENT SecondsEvent;
+ INT32 SecColumn;
+ INT32 SecRow;
+ UINT8 SaveChar;
+ UINT8 SecsLeft;
+
+ //
+ // if auto select, just get right to it
+ //
+ if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_AUTO_SELECT) {
+ return AUTO_SELECT;
+ }
+ //
+ // if no timeout, go directly to display of menu
+ //
+ if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_NO_TIMEOUT) {
+ return DO_MENU;
+ }
+ //
+ //
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ return DO_MENU;
+ }
+
+ Status = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ BootPromptPtr->Timeout * 10000000 + 100000
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return DO_MENU;
+ }
+ //
+ //
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &SecondsEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (TimeoutEvent);
+ return DO_MENU;
+ }
+
+ Status = gBS->SetTimer (
+ SecondsEvent,
+ TimerPeriodic,
+ 10000000
+ ); /* 1 second */
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (SecondsEvent);
+ gBS->CloseEvent (TimeoutEvent);
+ return DO_MENU;
+ }
+ //
+ // display the prompt
+ // IMPORTANT! This prompt is an ASCII character string that may
+ // not be terminated with a NULL byte.
+ //
+ SaveChar = BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1];
+ BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = 0;
+
+ AsciiPrint ("%a ", BootPromptPtr->Prompt);
+ BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = SaveChar;
+
+ //
+ // wait until time expires or selection made - menu or local
+ //
+ SecColumn = gST->ConOut->Mode->CursorColumn;
+ SecRow = gST->ConOut->Mode->CursorRow;
+ SecsLeft = BootPromptPtr->Timeout;
+
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
+ AsciiPrint ("(%d) ", SecsLeft);
+
+ //
+ // set the default action to be AUTO_SELECT
+ //
+ Status = AUTO_SELECT;
+
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ EFI_INPUT_KEY Key;
+
+ if (!EFI_ERROR (gBS->CheckEvent (SecondsEvent))) {
+ --SecsLeft;
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
+ AsciiPrint ("(%d) ", SecsLeft);
+ }
+
+ if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {
+ UINT8 Buffer[512];
+ UINTN BufferSize;
+ EFI_STATUS Status;
+
+ BufferSize = sizeof Buffer;
+
+ Status = Private->EfiBc.UdpRead (
+ &Private->EfiBc,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,
+ NULL, /* dest ip */
+ NULL, /* dest port */
+ NULL, /* src ip */
+ NULL, /* src port */
+ NULL, /* hdr size */
+ NULL, /* hdr ptr */
+ &BufferSize,
+ Buffer
+ );
+
+ continue;
+ }
+
+ if (Key.ScanCode == 0) {
+ switch (Key.UnicodeChar) {
+ case Ctl ('c'):
+ Status = LOCAL_BOOT;
+ break;
+
+ case Ctl ('m'):
+ case 'm':
+ case 'M':
+ Status = DO_MENU;
+ break;
+
+ default:
+ continue;
+ }
+ } else {
+ switch (Key.ScanCode) {
+ case SCAN_F8:
+ Status = DO_MENU;
+ break;
+
+ case SCAN_ESC:
+ Status = LOCAL_BOOT;
+ break;
+
+ default:
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ gBS->CloseEvent (SecondsEvent);
+ gBS->CloseEvent (TimeoutEvent);
+
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
+ AsciiPrint (" ");
+
+ return Status;
+}
+
+
+/**
+ Display one menu item.
+
+ @param MenuItemPtr Pointer to PXE menu item
+ option.
+
+ @return None
+
+**/
+STATIC
+VOID
+PrintMenuItem (
+ PXE_BOOT_MENU_ENTRY *MenuItemPtr
+ )
+{
+ UINT8 Length;
+ UINT8 SaveChar;
+
+ Length = (UINT8) MIN (70, MenuItemPtr->DataLen);
+ SaveChar = MenuItemPtr->Data[Length];
+
+ MenuItemPtr->Data[Length] = 0;
+ AsciiPrint (" %a\n", MenuItemPtr->Data);
+ MenuItemPtr->Data[Length] = SaveChar;
+}
+
+
+/**
+ Display and process menu.
+
+ @param Private Pointer to PxeBc interface
+ @param RxBufferPtr Pointer to receive buffer
+
+ @retval NO_MENU
+ @retval LOCAL_BOOT
+
+**/
+STATIC
+EFI_STATUS
+DoMenu (
+ PXE_BASECODE_DEVICE *Private,
+ DHCP_RECEIVE_BUFFER *RxBufferPtr
+ )
+{
+ PXE_OP_DISCOVERY_CONTROL *DiscoveryControlPtr;
+ PXE_BOOT_MENU_ENTRY *MenuItemPtrs[MAX_MENULIST];
+ EFI_STATUS Status;
+ UNION_PTR Ptr;
+ UINTN SaveNumRte;
+ UINTN TopRow;
+ UINTN MenuLth;
+ UINTN NumMenuItems;
+ UINTN Index;
+ UINTN Longest;
+ UINTN Selected;
+ UINT16 Type;
+ UINT16 Layer;
+ BOOLEAN Done;
+
+ Selected = 0;
+ Layer = 0;
+
+ DEBUG ((DEBUG_WARN, "\nDoMenu() Enter."));
+
+ /* see if we have a menu/prompt */
+ if (!(RxBufferPtr->OpAdds.Status & DISCOVER_TYPE)) {
+ DEBUG (
+ (DEBUG_WARN,
+ "\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ",
+ RxBufferPtr->OpAdds.Status)
+ );
+
+ return NO_MENU;
+ }
+
+ DiscoveryControlPtr = (PXE_OP_DISCOVERY_CONTROL *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];
+
+ //
+ // if not USE_BOOTFILE or no bootfile given, must have menu stuff
+ //
+ if ((DiscoveryControlPtr->ControlBits & USE_BOOTFILE) && RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
+ DEBUG ((DEBUG_WARN, "\nDoMenu() DHCP w/ bootfile. "));
+ return NO_MENU;
+ }
+ //
+ // do prompt & menu if necessary
+ //
+ Status = DoPrompt (Private, (PXE_OP_BOOT_PROMPT *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]);
+
+ if (Status == LOCAL_BOOT) {
+ DEBUG ((DEBUG_WARN, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. "));
+
+ return Status;
+ }
+
+ Ptr.BytePtr = (UINT8 *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];
+
+ MenuLth = Ptr.MenuPtr->Header.Length;
+ Ptr.CurrentMenuItemPtr = Ptr.MenuPtr->MenuItem;
+
+ //
+ // build menu items array
+ //
+ for (Longest = NumMenuItems = Index = 0; Index < MenuLth && NumMenuItems < MAX_MENULIST;) {
+ UINTN lth;
+
+ lth = Ptr.CurrentMenuItemPtr->DataLen + sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data);
+
+ MenuItemPtrs[NumMenuItems++] = Ptr.CurrentMenuItemPtr;
+
+ if (lth > Longest) {
+ //
+ // check if too long
+ //
+ if ((Longest = lth) > 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))) {
+ Longest = 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data));
+ }
+ }
+
+ Index += lth;
+ Ptr.BytePtr += lth;
+ }
+
+ if (Status != AUTO_SELECT) {
+ UINT8 BlankBuf[75];
+
+ SetMem (BlankBuf, sizeof BlankBuf, ' ');
+ BlankBuf[Longest + 5 - (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))] = 0;
+ AsciiPrint ("\n");
+
+ //
+ // now put up menu
+ //
+ for (Index = 0; Index < NumMenuItems; ++Index) {
+ PrintMenuItem (MenuItemPtrs[Index]);
+ }
+
+ TopRow = gST->ConOut->Mode->CursorRow - NumMenuItems;
+
+ //
+ // now wait for a selection
+ //
+ Done = FALSE;
+ do {
+ //
+ // highlight selection
+ //
+ EFI_INPUT_KEY Key;
+ UINTN NewSelected;
+
+ NewSelected = Selected;
+
+ //
+ // highlight selected row
+ //
+ gST->ConOut->SetAttribute (
+ gST->ConOut,
+ EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)
+ );
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Selected);
+
+ AsciiPrint (" --->%a\r", BlankBuf);
+
+ PrintMenuItem (MenuItemPtrs[Selected]);
+ gST->ConOut->SetAttribute (
+ gST->ConOut,
+ EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
+ );
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + NumMenuItems);
+
+ //
+ // wait for a keystroke
+ //
+ while (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {
+ UINT8 TmpBuf[512];
+ UINTN TmpBufLen;
+
+ TmpBufLen = sizeof TmpBuf;
+
+ Private->EfiBc.UdpRead (
+ &Private->EfiBc,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,
+ NULL, /* dest ip */
+ NULL, /* dest port */
+ NULL, /* src ip */
+ NULL, /* src port */
+ NULL, /* hdr size */
+ NULL, /* hdr ptr */
+ &TmpBufLen,
+ TmpBuf
+ );
+ }
+
+ if (!Key.ScanCode) {
+ switch (Key.UnicodeChar) {
+ case Ctl ('c'):
+ Key.ScanCode = SCAN_ESC;
+ break;
+
+ case Ctl ('j'): /* linefeed */
+ case Ctl ('m'): /* return */
+ Done = TRUE;
+ break;
+
+ case Ctl ('i'): /* tab */
+ case ' ':
+ case 'd':
+ case 'D':
+ Key.ScanCode = SCAN_DOWN;
+ break;
+
+ case Ctl ('h'): /* backspace */
+ case 'u':
+ case 'U':
+ Key.ScanCode = SCAN_UP;
+ break;
+
+ default:
+ Key.ScanCode = 0;
+ }
+ }
+
+ switch (Key.ScanCode) {
+ case SCAN_LEFT:
+ case SCAN_UP:
+ if (NewSelected) {
+ --NewSelected;
+ }
+
+ break;
+
+ case SCAN_DOWN:
+ case SCAN_RIGHT:
+ if (++NewSelected == NumMenuItems) {
+ --NewSelected;
+ }
+
+ break;
+
+ case SCAN_PAGE_UP:
+ case SCAN_HOME:
+ NewSelected = 0;
+ break;
+
+ case SCAN_PAGE_DOWN:
+ case SCAN_END:
+ NewSelected = NumMenuItems - 1;
+ break;
+
+ case SCAN_ESC:
+ return LOCAL_BOOT;
+ }
+
+ /* unhighlight last selected row */
+ gST->ConOut->SetCursorPosition (gST->ConOut, 5, TopRow + Selected);
+
+ AsciiPrint ("%a\r", BlankBuf);
+
+ PrintMenuItem (MenuItemPtrs[Selected]);
+
+ Selected = NewSelected;
+ } while (!Done);
+ }
+
+ SaveNumRte = Private->EfiBc.Mode->RouteTableEntries;
+
+ Type = NTOHS (MenuItemPtrs[Selected]->Type);
+
+ if (Type == 0) {
+ DEBUG ((DEBUG_WARN, "\nDoMenu() Local boot selected. "));
+ return LOCAL_BOOT;
+ }
+
+ AsciiPrint ("Discover");
+
+ Status = Private->EfiBc.Discover (
+ &Private->EfiBc,
+ Type,
+ &Layer,
+ (BOOLEAN) (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected),
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ AsciiPrint ("\r \r");
+
+ DEBUG (
+ (DEBUG_WARN,
+ "\nDoMenu() Return w/ %xh (%r).",
+ Status,
+ Status)
+ );
+
+ return Status;
+ }
+
+ AsciiPrint ("\rBOOT_SERVER_IP: ");
+ PrintIpv4 ((UINT8 *) &Private->ServerIp);
+
+ for (Index = SaveNumRte; Index < Private->EfiBc.Mode->RouteTableEntries; ++Index) {
+ if ((Index % 3) == 0) {
+ AsciiPrint ("\r\nGATEWAY IP:");
+ }
+
+ AsciiPrint (" ");
+ PrintIpv4 ((UINT8 *) &Private->EfiBc.Mode->RouteTable[Index].GwAddr);
+ AsciiPrint (" ");
+ }
+
+ AsciiPrint ("\n");
+
+ DEBUG ((DEBUG_WARN, "\nDoMenu() Return w/ EFI_SUCCESS. "));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get value 8- or 16-bit value from DHCP option.
+
+ @param OpPtr Pointer to DHCP option
+
+ @return Value from DHCP option
+
+**/
+STATIC
+UINT16
+GetValue (
+ DHCPV4_OP_STRUCT *OpPtr
+ )
+{
+ if (OpPtr->Header.Length == 1) {
+ return OpPtr->Data[0];
+ } else {
+ return NTOHS (OpPtr->Data);
+ }
+}
+
+
+/**
+ Locate opcode in buffer.
+
+ @param BufferPtr Pointer to buffer
+ @param BufferLen Length of buffer
+ @param OpCode Option number
+
+ @return Pointer to opcode, may be NULL
+
+**/
+STATIC
+UINT8 *
+_PxeBcFindOpt (
+ UINT8 *BufferPtr,
+ UINTN BufferLen,
+ UINT8 OpCode
+ )
+{
+ if (BufferPtr == NULL) {
+ return NULL;
+ }
+
+ while (BufferLen != 0) {
+ if (*BufferPtr == OpCode) {
+ return BufferPtr;
+ }
+
+ switch (*BufferPtr) {
+ case OP_END:
+ return NULL;
+
+ case OP_PAD:
+ ++BufferPtr;
+ --BufferLen;
+ continue;
+ }
+
+ if ((UINTN) BufferLen <= (UINTN) 2 + BufferPtr[1]) {
+ return NULL;
+ }
+
+ BufferLen -= 2 + BufferPtr[1];
+ BufferPtr += 2 + BufferPtr[1];
+ }
+
+ return NULL;
+}
+
+
+/**
+ Find option in packet
+
+ @param PacketPtr Pointer to packet
+ @param OpCode option number
+
+ @return Pointer to option in packet
+
+**/
+STATIC
+UINT8 *
+PxeBcFindDhcpOpt (
+ EFI_PXE_BASE_CODE_PACKET *PacketPtr,
+ UINT8 OpCode
+ )
+{
+ UINTN PacketLen;
+ UINT8 Overload;
+ UINT8 *OptionBufferPtr;
+
+ //
+ //
+ //
+ PacketLen = 380;
+ Overload = 0;
+
+ //
+ // Figure size of DHCP option space.
+ //
+ OptionBufferPtr = _PxeBcFindOpt (
+ PacketPtr->Dhcpv4.DhcpOptions,
+ 380,
+ OP_DHCP_MAX_MESSAGE_SZ
+ );
+
+ if (OptionBufferPtr != NULL) {
+ if (OptionBufferPtr[1] == 2) {
+ UINT16 n;
+
+ CopyMem (&n, &OptionBufferPtr[2], 2);
+ PacketLen = HTONS (n);
+
+ if (PacketLen < sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET)) {
+ PacketLen = 380;
+ } else {
+ PacketLen -= (PacketPtr->Dhcpv4.DhcpOptions - &PacketPtr->Dhcpv4.BootpOpcode) + 28;
+ }
+ }
+ }
+ //
+ // Look for option overloading.
+ //
+ OptionBufferPtr = _PxeBcFindOpt (
+ PacketPtr->Dhcpv4.DhcpOptions,
+ PacketLen,
+ OP_DHCP_OPTION_OVERLOAD
+ );
+
+ if (OptionBufferPtr != NULL) {
+ if (OptionBufferPtr[1] == 1) {
+ Overload = OptionBufferPtr[2];
+ }
+ }
+ //
+ // Look for caller's option.
+ //
+ OptionBufferPtr = _PxeBcFindOpt (
+ PacketPtr->Dhcpv4.DhcpOptions,
+ PacketLen,
+ OpCode
+ );
+
+ if (OptionBufferPtr != NULL) {
+ return OptionBufferPtr;
+ }
+
+ if (Overload & OVLD_FILE) {
+ OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpBootFile, 128, OpCode);
+
+ if (OptionBufferPtr != NULL) {
+ return OptionBufferPtr;
+ }
+ }
+
+ if (Overload & OVLD_SRVR_NAME) {
+ OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpSrvName, 64, OpCode);
+
+ if (OptionBufferPtr != NULL) {
+ return OptionBufferPtr;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Download file into buffer
+
+ @param Private Pointer to PxeBc interface
+ @param BufferSize pointer to size of download
+ buffer
+ @param Buffer Pointer to buffer
+
+ @return EFI_BUFFER_TOO_SMALL -
+ @return EFI_NOT_FOUND -
+ @return EFI_PROTOCOL_ERROR -
+
+**/
+STATIC
+EFI_STATUS
+DownloadFile (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN OUT UINT64 *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo;
+ EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode;
+ DHCP_RECEIVE_BUFFER *RxBuf;
+ EFI_STATUS Status;
+ UINTN BlockSize;
+
+ RxBuf = (DHCP_RECEIVE_BUFFER *) Private->BootServerReceiveBuffer;
+ BlockSize = 0x8000;
+
+ DEBUG ((EFI_D_WARN, "\nDownloadFile() Enter."));
+
+ if (Buffer == NULL || *BufferSize == 0 || *BufferSize < Private->FileSize) {
+ if (Private->FileSize != 0) {
+ *BufferSize = Private->FileSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ AsciiPrint ("\nTSize");
+
+ OpCode = EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE;
+ } else if (RxBuf->OpAdds.Status & WfM11a_TYPE) {
+ OpCode = EFI_PXE_BASE_CODE_MTFTP_READ_FILE;
+
+ ZeroMem (&MtftpInfo, sizeof MtftpInfo);
+
+ *(IPV4_ADDR *) &MtftpInfo.MCastIp = *(IPV4_ADDR *) RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_IP - 1]->Data;
+
+ CopyMem (
+ &MtftpInfo.CPort,
+ RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_CPORT - 1]->Data,
+ sizeof MtftpInfo.CPort
+ );
+
+ CopyMem (
+ &MtftpInfo.SPort,
+ RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_SPORT - 1]->Data,
+ sizeof MtftpInfo.SPort
+ );
+
+ MtftpInfo.ListenTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_TMOUT - 1]);
+
+ MtftpInfo.TransmitTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_DELAY - 1]);
+
+ AsciiPrint ("\nMTFTP");
+ } else {
+ AsciiPrint ("\nTFTP");
+
+ OpCode = EFI_PXE_BASE_CODE_TFTP_READ_FILE;
+ }
+
+ Private->FileSize = 0;
+
+ RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data[RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length] = 0;
+
+ Status = Private->EfiBc.Mtftp (
+ &Private->EfiBc,
+ OpCode,
+ Buffer,
+ FALSE,
+ BufferSize,
+ &BlockSize,
+ &Private->ServerIp,
+ (UINT8 *) RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data,
+ &MtftpInfo,
+ FALSE
+ );
+
+ if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
+ DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #1 %Xh", Status));
+ return Status;
+ }
+
+ if (sizeof (UINTN) < sizeof (UINT64) && *BufferSize > 0xFFFFFFFF) {
+ Private->FileSize = 0xFFFFFFFF;
+ } else {
+ Private->FileSize = (UINTN) *BufferSize;
+ }
+
+ if (OpCode == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {
+ DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #2"));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #3 %Xh", Status));
+ return Status;
+ }
+
+ if (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected && Private->EfiBc.Mode->PxeBisReplyReceived) {
+ UINT64 CredentialLen;
+ UINTN BlockSize;
+ UINT8 CredentialFilename[256];
+ UINT8 *op;
+ VOID *CredentialBuffer;
+
+ //
+ // Get name of credential file. It may be in the BOOTP
+ // bootfile field or a DHCP option.
+ //
+ ZeroMem (CredentialFilename, sizeof CredentialFilename);
+
+ op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_DHCP_BOOTFILE);
+
+ if (op != NULL) {
+ if (op[1] == 0) {
+ /* No credential filename */
+ return EFI_NOT_FOUND;
+ }
+
+ CopyMem (CredentialFilename, &op[2], op[1]);
+ } else {
+ if (Private->EfiBc.Mode->PxeBisReply.Dhcpv4.BootpBootFile[0] == 0) {
+ /* No credential filename */
+ return EFI_NOT_FOUND;
+ }
+
+ CopyMem (CredentialFilename, &op[2], 128);
+ }
+ //
+ // Get size of credential file. It may be available as a
+ // DHCP option. If not, use the TFTP get file size.
+ //
+ CredentialLen = 0;
+
+ op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_BOOT_FILE_SZ);
+
+ if (op != NULL) {
+ /*
+ * This is actually the size of the credential file
+ * buffer. The actual credential file size will be
+ * returned when we download the file.
+ */
+ if (op[1] == 2) {
+ UINT16 n;
+
+ CopyMem (&n, &op[2], 2);
+ CredentialLen = HTONS (n) * 512;
+ }
+ }
+
+ if (CredentialLen == 0) {
+ BlockSize = 8192;
+
+ Status = Private->EfiBc.Mtftp (
+ &Private->EfiBc,
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+ NULL,
+ FALSE,
+ &CredentialLen,
+ &BlockSize,
+ &Private->ServerIp,
+ CredentialFilename,
+ NULL,
+ FALSE
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (CredentialLen == 0) {
+ //
+ // %%TBD -- EFI error for invalid credential
+ // file.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ }
+ //
+ // Allocate credential file buffer.
+ //
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ (UINTN) CredentialLen,
+ &CredentialBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Download credential file.
+ //
+ BlockSize = 8192;
+
+ Status = Private->EfiBc.Mtftp (
+ &Private->EfiBc,
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+ CredentialBuffer,
+ FALSE,
+ &CredentialLen,
+ &BlockSize,
+ &Private->ServerIp,
+ CredentialFilename,
+ NULL,
+ FALSE
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (CredentialBuffer);
+ return Status;
+ }
+ //
+ // Verify credentials.
+ //
+ if (PxebcBisVerify (Private, Buffer, Private->FileSize, CredentialBuffer, (UINTN) CredentialLen)) {
+ Status = EFI_SUCCESS;
+ } else {
+ //
+ // %%TBD -- An EFI error code for failing credential verification.
+ //
+ Status = EFI_PROTOCOL_ERROR;
+ }
+
+ gBS->FreePool (CredentialBuffer);
+ }
+
+ return Status;
+}
+
+
+/**
+ Start PXE DHCP. Get DHCP and proxyDHCP information.
+ Display remote boot menu and prompt. Select item from menu.
+
+ @param Private Pointer to PxeBc interface
+ @param BufferSize Pointer to download buffer
+ size
+ @param Buffer Pointer to download buffer
+
+ @retval EFI_SUCCESS
+ @retval EFI_NOT_READY
+
+**/
+STATIC
+EFI_STATUS
+LoadfileStart (
+ IN PXE_BASECODE_DEVICE *Private,
+ IN OUT UINT64 *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_STATUS Status;
+ VOID *RxBuf;
+
+ DEBUG ((DEBUG_WARN, "\nLoadfileStart() Enter."));
+
+ //
+ // Try to start BaseCode, for now only IPv4 is supported
+ // so don't try to start using IPv6.
+ //
+ Status = Private->EfiBc.Start (&Private->EfiBc, FALSE);
+
+ if (EFI_ERROR (Status)) {
+ if (Status != EFI_ALREADY_STARTED) {
+ DEBUG ((DEBUG_NET, "\nLoadfileStart() Exit BC.Start() == %xh", Status));
+ return Status;
+ }
+ }
+ //
+ // Get pointers to PXE mode structure, SNP protocol structure
+ // and SNP mode structure.
+ //
+ PxeBcMode = Private->EfiBc.Mode;
+ Snp = Private->SimpleNetwork;
+ SnpMode = Snp->Mode;
+
+ //
+ // Display client MAC address, like 16-bit PXE ROMs
+ //
+ AsciiPrint ("\nCLIENT MAC ADDR: ");
+
+ {
+ UINTN Index;
+ UINTN hlen;
+
+ hlen = SnpMode->HwAddressSize;
+
+ for (Index = 0; Index < hlen; ++Index) {
+ AsciiPrint ("%02x ", SnpMode->CurrentAddress.Addr[Index]);
+ }
+ }
+
+ AsciiPrint ("\nDHCP");
+
+ Status = Private->EfiBc.Dhcp (&Private->EfiBc, TRUE);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status));
+ AsciiPrint ("\r \r");
+ return Status;
+ }
+
+ ShowMyInfo (Private);
+
+ RxBuf = PxeBcMode->ProxyOfferReceived ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER;
+#define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
+
+ Status = DoMenu (Private, RxBufferPtr);
+
+ if (Status == EFI_SUCCESS) {
+ //
+ // did a discovery - take info from discovery packet
+ //
+ RxBuf = &PXE_ACK_BUFFER;
+ } else if (Status == NO_MENU) {
+ //
+ // did not do a discovery - take info from rxbuf
+ //
+ Private->ServerIp.Addr[0] = RxBufferPtr->u.Dhcpv4.siaddr;
+
+ if (!(Private->ServerIp.Addr[0])) {
+ *(IPV4_ADDR *) &Private->ServerIp = *(IPV4_ADDR *) RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]->Data;
+ }
+ } else {
+ DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit DoMenu() == %Xh", Status));
+ return Status;
+ }
+
+ if (!RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
+ DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit Not ready?"));
+ return EFI_NOT_READY;
+ }
+ //
+ // check for file size option sent
+ //
+ if (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]) {
+ Private->FileSize = 512 * NTOHS (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]->Data);
+ }
+
+ Private->BootServerReceiveBuffer = RxBufferPtr;
+
+ Status = DownloadFile (Private, BufferSize, Buffer);
+
+ DEBUG (
+ (DEBUG_WARN,
+ "\nLoadfileStart() Exit. DownloadFile() = %Xh",
+ Status)
+ );
+
+ return Status;
+}
+
+
+/**
+ Loadfile interface for PxeBc interface
+
+ @param This Pointer to Loadfile interface
+ @param FilePath Not used and not checked
+ @param BootPolicy Must be TRUE
+ @param BufferSize Pointer to buffer size
+ @param Buffer Pointer to download buffer or
+ NULL
+
+ @return EFI_INVALID_PARAMETER -
+ @return EFI_UNSUPPORTED -
+ @return EFI_SUCCESS -
+ @return EFI_BUFFER_TOO_SMALL -
+
+**/
+EFI_STATUS
+EFIAPI
+LoadFile (
+ IN EFI_LOAD_FILE_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ IN OUT VOID *Buffer
+ )
+{
+ LOADFILE_DEVICE *LoadfilePtr;
+ UINT64 TmpBufSz;
+ INT32 OrigMode;
+ INT32 OrigAttribute;
+ BOOLEAN RemoveCallback;
+ BOOLEAN NewMakeCallback;
+ EFI_STATUS Status;
+ EFI_STATUS TempStatus;
+ //
+ //
+ //
+ OrigMode = gST->ConOut->Mode->Mode;
+ OrigAttribute = gST->ConOut->Mode->Attribute;
+ RemoveCallback = FALSE;
+
+ AsciiPrint ("Running LoadFile()\n");
+
+ //
+ // Resolve Warning 4 unreferenced parameter problem
+ //
+ FilePath = NULL;
+
+ //
+ // If either if these parameters are NULL, we cannot continue.
+ //
+ if (This == NULL || BufferSize == NULL) {
+ DEBUG ((DEBUG_WARN, "\nLoadFile() This or BufferSize == NULL"));
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // We only support BootPolicy == TRUE
+ //
+ if (!BootPolicy) {
+ DEBUG ((DEBUG_WARN, "\nLoadFile() BootPolicy == FALSE"));
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Get pointer to LoadFile protocol structure.
+ //
+ LoadfilePtr = CR (This, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE);
+
+ if (LoadfilePtr == NULL) {
+ DEBUG (
+ (DEBUG_NET,
+ "\nLoadFile() Could not get pointer to LoadFile structure")
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Lock interface
+ //
+ EfiAcquireLock (&LoadfilePtr->Lock);
+
+ //
+ // Set console output mode and display attribute
+ //
+ if (OrigMode != 0) {
+ gST->ConOut->SetMode (gST->ConOut, 0);
+ }
+
+ gST->ConOut->SetAttribute (
+ gST->ConOut,
+ EFI_TEXT_ATTR (EFI_LIGHTGRAY,EFI_BLACK)
+ );
+
+ //
+ // See if BaseCode already has a Callback protocol attached.
+ // If there is none, attach our own Callback protocol.
+ //
+ Status = gBS->HandleProtocol (
+ LoadfilePtr->Private->Handle,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ (VOID *) &LoadfilePtr->Private->CallbackProtocolPtr
+ );
+
+ if (Status == EFI_SUCCESS) {
+ //
+ // There is already a callback routine. Do nothing.
+ //
+ DEBUG ((DEBUG_WARN, "\nLoadFile() BC callback exists."));
+
+ } else if (Status == EFI_UNSUPPORTED) {
+ //
+ // No BaseCode Callback protocol found. Add our own.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &LoadfilePtr->Private->Handle,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &_bc_callback
+ );
+
+ DEBUG ((DEBUG_WARN, "\nLoadFile() Callback install status == %xh", Status));
+
+ RemoveCallback = (BOOLEAN) (Status == EFI_SUCCESS);
+
+ if (LoadfilePtr->Private->EfiBc.Mode != NULL && LoadfilePtr->Private->EfiBc.Mode->Started) {
+ NewMakeCallback = TRUE;
+ LoadfilePtr->Private->EfiBc.SetParameters (
+ &LoadfilePtr->Private->EfiBc,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &NewMakeCallback
+ );
+ }
+
+ } else {
+ DEBUG ((DEBUG_WARN, "\nLoadFile() Callback check status == %xh", Status));
+ }
+ //
+ // Check for starting or for continuing after already getting
+ // the file size.
+ //
+ if (LoadfilePtr->Private->FileSize == 0) {
+ TmpBufSz = 0;
+ Status = LoadfileStart (LoadfilePtr->Private, &TmpBufSz, Buffer);
+
+ if (sizeof (UINTN) < sizeof (UINT64) && TmpBufSz > 0xFFFFFFFF) {
+ *BufferSize = 0xFFFFFFFF;
+ } else {
+ *BufferSize = (UINTN) TmpBufSz;
+ }
+
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // This is done so loadfile will work even if the boot manager
+ // did not make the first call with Buffer == NULL.
+ //
+ Buffer = NULL;
+ }
+ } else if (Buffer == NULL) {
+ DEBUG ((DEBUG_WARN, "\nLoadfile() Get buffer size"));
+
+ //
+ // Continuing from previous LoadFile request. Make sure there
+ // is a buffer and that it is big enough.
+ //
+ *BufferSize = LoadfilePtr->Private->FileSize;
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+ DEBUG ((DEBUG_WARN, "\nLoadFile() Download file"));
+
+ //
+ // Everything looks good, try to download the file.
+ //
+ TmpBufSz = *BufferSize;
+ Status = DownloadFile (LoadfilePtr->Private, &TmpBufSz, Buffer);
+
+ //
+ // Next call to loadfile will start DHCP process again.
+ //
+ LoadfilePtr->Private->FileSize = 0;
+ }
+ //
+ // If we added a callback protocol, now is the time to remove it.
+ //
+ if (RemoveCallback) {
+ NewMakeCallback = FALSE;
+ TempStatus = LoadfilePtr->Private->EfiBc.SetParameters (
+ &LoadfilePtr->Private->EfiBc,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &NewMakeCallback
+ );
+
+ if (TempStatus == EFI_SUCCESS) {
+ gBS->UninstallProtocolInterface (
+ LoadfilePtr->Private->Handle,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ &_bc_callback
+ );
+ }
+ }
+ //
+ // Restore display mode and attribute
+ //
+ if (OrigMode != 0) {
+ gST->ConOut->SetMode (gST->ConOut, OrigMode);
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, OrigAttribute);
+
+ //
+ // Unlock interface
+ //
+ EfiReleaseLock (&LoadfilePtr->Lock);
+
+ DEBUG ((DEBUG_WARN, "\nBC.Loadfile() Status == %xh\n", Status));
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_SUCCESS;
+
+ } else if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // Error is only displayed when we are actually trying to
+ // download the boot image.
+ //
+ if (Buffer == NULL) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
+
+ } else if (Status == EFI_DEVICE_ERROR) {
+ AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n");
+
+ } else if (Status == EFI_OUT_OF_RESOURCES) {
+ AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
+
+ } else if (Status == EFI_NO_MEDIA) {
+ AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n");
+
+ } else if (Status == EFI_NO_RESPONSE) {
+ AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
+
+ } else if (Status == EFI_TIMEOUT) {
+ AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n");
+
+ } else if (Status == EFI_ABORTED) {
+ AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
+
+ } else if (Status == EFI_ICMP_ERROR) {
+ AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
+
+ if (LoadfilePtr->Private->EfiBc.Mode != NULL) {
+ if (LoadfilePtr->Private->EfiBc.Mode->IcmpErrorReceived) {
+
+ AsciiPrint (
+ "PXE-E98: Type: %xh Code: %xh ",
+ LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type,
+ LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code
+ );
+
+ switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type) {
+ case 0x03:
+ switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code) {
+ case 0x00: /* net unreachable */
+ AsciiPrint ("Net unreachable");
+ break;
+
+ case 0x01: /* host unreachable */
+ AsciiPrint ("Host unreachable");
+ break;
+
+ case 0x02: /* protocol unreachable */
+ AsciiPrint ("Protocol unreachable");
+ break;
+
+ case 0x03: /* port unreachable */
+ AsciiPrint ("Port unreachable");
+ break;
+
+ case 0x04: /* Fragmentation needed */
+ AsciiPrint ("Fragmentation needed");
+ break;
+
+ case 0x05: /* Source route failed */
+ AsciiPrint ("Source route failed");
+ break;
+ }
+
+ break;
+ }
+
+ AsciiPrint ("\n");
+ }
+ }
+
+ } else if (Status == EFI_TFTP_ERROR) {
+ AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
+
+ if (LoadfilePtr->Private->EfiBc.Mode != NULL) {
+ if (LoadfilePtr->Private->EfiBc.Mode->TftpErrorReceived) {
+ AsciiPrint (
+ "PXE-E98: Code: %xh %a\n",
+ LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorCode,
+ LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorString
+ );
+ }
+ }
+
+ } else {
+ AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status);
+ }
+
+ LoadfilePtr->Private->EfiBc.Stop (&LoadfilePtr->Private->EfiBc);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h
new file mode 100644
index 0000000000..f81d86c163
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h
@@ -0,0 +1,154 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ tftp.h
+
+Abstract:
+
+
+**/
+
+#ifndef __TFTP_H__
+#define __TFTP_H__
+
+//
+// Definitions for trivial file transfer protocol functionality with IP v4
+// Per RFC 1350, July 1992 and RFC 2347, 8, and 9, May 1998
+//
+#pragma pack(1)
+//
+// max and min packet sizes
+// (all data packets in transmission except last)
+//
+#define MAX_TFTP_PKT_SIZE (BUFFER_ALLOCATE_SIZE - 512)
+#define MIN_TFTP_PKT_SIZE 512
+
+//
+// TFTPv4 OpCodes
+//
+#define TFTP_RRQ 1 // read request
+#define TFTP_WRQ 2 // write request
+#define TFTP_DATA 3 // data
+#define TFTP_ACK 4 // acknowledgement
+#define TFTP_ERROR 5 // error packet
+#define TFTP_OACK 6 // option acknowledge
+#define TFTP_DIR 7 // read directory request
+#define TFTP_DATA8 8
+#define TFTP_ACK8 9
+
+//
+// request packet (read or write)
+// Fields shown (except file name) are not to be referenced directly,
+// since their placement is variable within a request packet.
+// All are null terminated case insensitive ascii strings.
+//
+struct Tftpv4Req {
+ UINT16 OpCode; // TFTP Op code
+ UINT8 FileName[2]; // file name
+ UINT8 Mode[2]; // "netascii" or "octet"
+ struct { // optionally, one or more option requests
+ UINT8 Option[2]; // option name
+ UINT8 Value[2]; // value requested
+ } OpReq[1];
+};
+
+//
+// modes
+//
+#define MODE_ASCII "netascii"
+#define MODE_BINARY "octet"
+
+//
+// option strings
+//
+#define OP_BLKSIZE "blksize" // block size option
+#define OP_TIMEOUT "timeout" // time to wait before retransmitting
+#define OP_TFRSIZE "tsize" // total transfer size option
+#define OP_OVERWRITE "overwrite" // overwrite file option
+#define OP_BIGBLKNUM "bigblk#" // big block number
+// See RFC 2347, 8, and 9 for more information on TFTP options
+// option acknowledge packet (optional)
+// options not acknowledged are rejected
+//
+struct Tftpv4Oack {
+ UINT16 OpCode; // TFTP Op code
+ struct { // optionally, one or more option acknowledgements
+ UINT8 Option[2]; // option name (of those requested)
+ UINT8 Value[2]; // value acknowledged
+ } OpAck[1];
+};
+
+//
+// acknowledge packet
+//
+struct Tftpv4Ack {
+ UINT16 OpCode; // TFTP Op code
+ UINT16 BlockNum;
+};
+
+//
+// data packet
+//
+struct Tftpv4Data {
+ struct Tftpv4Ack Header;
+ UINT8 Data[512];
+};
+
+//
+// big block number ack packet
+//
+struct Tftpv4Ack8 {
+ UINT16 OpCode;
+ UINT64 BlockNum;
+};
+
+//
+// big block number data packet
+//
+struct Tftpv4Data8 {
+ struct Tftpv4Ack8 Header;
+ UINT8 Data[506];
+};
+
+//
+// error packet
+//
+struct Tftpv4Error {
+ UINT16 OpCode; // TFTP Op code
+ UINT16 ErrCode; // error code
+ UINT8 ErrMsg[1]; // error message (nul terminated)
+};
+
+#pragma pack()
+//
+// error codes
+//
+#define TFTP_ERR_UNDEF 0 // Not defined, see error message (if any).
+#define TFTP_ERR_NOT_FOUND 1 // File not found.
+#define TFTP_ERR_ACCESS 2 // Access violation.
+#define TFTP_ERR_FULL 3 // Disk full or allocation exceeded.
+#define TFTP_ERR_ILLEGAL 4 // Illegal TFTP operation.
+#define TFTP_ERR_BAD_ID 5 // Unknown transfer ID.
+#define TFTP_ERR_EXISTS 6 // File already exists.
+#define TFTP_ERR_NO_USER 7 // No such user.
+#define TFTP_ERR_OPTION 8 // Option negotiation termination
+//
+// some defines
+//
+#define REQ_RESP_TIMEOUT 5 // Wait five seconds for request response.
+#define ACK_TIMEOUT 4 // Wait four seconds for ack response.
+#define NUM_ACK_RETRIES 3
+#define NUM_MTFTP_OPEN_RETRIES 3
+
+#endif /* __TFTP_H__ */
+
+/* EOF - tftp.h */
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h
new file mode 100644
index 0000000000..a8fa293a16
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h
@@ -0,0 +1,26 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeArch.h
+
+Abstract:
+ Defines PXE Arch type
+
+
+**/
+
+#ifndef _EFI_PXE_ARCH_H_
+#define _EFI_PXE_ARCH_H_
+
+#define SYS_ARCH 0x7
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..7186a9f159
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c
@@ -0,0 +1,169 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ ComponentName.c
+
+Abstract:
+ PxeDhcp4 component name protocol declarations
+
+
+**/
+
+#include "PxeDhcp4.h"
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+PxeDhcp4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+PxeDhcp4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gPxeDhcp4ComponentName = {
+ PxeDhcp4ComponentNameGetDriverName,
+ PxeDhcp4ComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mPxeDhcp4DriverNameTable[] = {
+ {
+ "eng",
+ L"PXE DHCPv4 Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+EFIAPI
+PxeDhcp4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gPxeDhcp4ComponentName.SupportedLanguages,
+ mPxeDhcp4DriverNameTable,
+ DriverName
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+EFIAPI
+PxeDhcp4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
+
+/* EOF - ComponentName.c */
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c
new file mode 100644
index 0000000000..fe4da51ea3
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c
@@ -0,0 +1,355 @@
+/** @file
+
+Copyright (c) 2004 - 2005, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4.c
+
+Abstract:
+
+
+**/
+
+
+#include "PxeDhcp4.h"
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// Prototypes
+// Driver model protocol interface
+//
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// PXE DHCP Protocol Interface
+//
+EFI_DRIVER_BINDING_PROTOCOL gPxeDhcp4DriverBinding = {
+ PxeDhcp4DriverBindingSupported,
+ PxeDhcp4DriverBindingStart,
+ PxeDhcp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// PxeDhcp4 Driver Entry point funtion
+//
+
+/**
+ Register Driver Binding protocol for this driver.
+
+ @param entry EFI_IMAGE_ENTRY_POINT)
+
+ @retval EFI_SUCCESS Driver loaded.
+ @retval other Driver not loaded.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gPxeDhcp4DriverBinding,
+ NULL,
+ COMPONENT_NAME,
+ NULL,
+ NULL
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+ Test to see if this driver supports ControllerHandle. Any
+ ControllerHandle that contains a PxeBaseCode protocol can be
+ supported.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param RemainingDevicePath Not used.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+
+ //
+ // Open the IO Abstraction(s) needed to perform the supported test.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Close the I/O Abstraction(s) used to perform the supported test.
+ //
+ return gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+ Start this driver on ControllerHandle by opening a PxeBaseCode
+ protocol and installing a PxeDhcp4 protocol on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to.
+ @param RemainingDevicePath Not used, always produce all possible children.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ PXE_DHCP4_PRIVATE_DATA *Private;
+
+ //
+ // Connect to the PxeBaseCode interface on ControllerHandle.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // BaseCode has already grabbed the SimpleNetwork interface
+ // so just do a HandleProtocol() to get it.
+ //
+ Status = gBS->HandleProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &Snp
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto error_exit;
+ }
+
+ ASSERT (Snp);
+
+ //
+ // Initialize the PXE DHCP device instance.
+ //
+ Private = AllocateZeroPool (sizeof (PXE_DHCP4_PRIVATE_DATA));
+ if (Private == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto error_exit;
+ }
+
+ Private->Signature = PXE_DHCP4_PRIVATE_DATA_SIGNATURE;
+ Private->PxeBc = PxeBc;
+ Private->Snp = Snp;
+ Private->Handle = ControllerHandle;
+ Private->PxeDhcp4.Revision = EFI_PXE_DHCP4_PROTOCOL_REVISION;
+ Private->PxeDhcp4.Run = PxeDhcp4Run;
+ Private->PxeDhcp4.Setup = PxeDhcp4Setup;
+ Private->PxeDhcp4.Init = PxeDhcp4Init;
+ Private->PxeDhcp4.Select = PxeDhcp4Select;
+ Private->PxeDhcp4.Renew = PxeDhcp4Renew;
+ Private->PxeDhcp4.Rebind = PxeDhcp4Rebind;
+ Private->PxeDhcp4.Release = PxeDhcp4Release;
+ Private->PxeDhcp4.Data = NULL;
+
+ //
+ // Install protocol interfaces for the PXE DHCP device.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ControllerHandle,
+ &gEfiPxeDhcp4ProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->PxeDhcp4
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+error_exit: ;
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ return Status;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+ Stop this driver on ControllerHandle by removing PXE DHCP
+ protocol and closing the PXE Base Code protocol on
+ ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on.
+ @param NumberOfChildren Not used.
+ @param ChildHandleBuffer Not used.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_PXE_DHCP4_PROTOCOL *PxeDhcp4;
+ PXE_DHCP4_PRIVATE_DATA *Private;
+
+ //
+ // Get our context back.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPxeDhcp4ProtocolGuid,
+ (VOID **) &PxeDhcp4,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (PxeDhcp4);
+
+ //
+ // Release allocated resources
+ //
+ if (Private->PxeDhcp4.Data) {
+ FreePool (Private->PxeDhcp4.Data);
+ Private->PxeDhcp4.Data = NULL;
+ }
+ //
+ // Uninstall our protocol
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ControllerHandle,
+ &gEfiPxeDhcp4ProtocolGuid,
+ &Private->PxeDhcp4
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Close any consumed protocols
+ //
+ Status = gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Release our private data
+ //
+ FreePool (Private);
+
+ return Status;
+}
+
+/* EOF - PxeDhcp4.c */
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h
new file mode 100644
index 0000000000..b33ed3e5bc
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h
@@ -0,0 +1,353 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4.h
+
+Abstract:
+ Common header for PxeDhcp4 protocol driver
+
+
+**/
+#ifndef _PXEDHCP4_H
+#define _PXEDHCP4_H
+
+
+#include <PiDxe.h>
+
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/PxeDhcp4.h>
+#include <Protocol/PxeDhcp4Callback.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// PxeDhcp4 protocol instance data
+//
+typedef struct {
+ //
+ // Signature field used to locate beginning of containment record.
+ //
+ UINTN Signature;
+
+#define PXE_DHCP4_PRIVATE_DATA_SIGNATURE EFI_SIGNATURE_32 ('p', 'x', 'D', '4')
+ //
+ // Device handle the protocol is bound to.
+ //
+ EFI_HANDLE Handle;
+
+ //
+ // Public PxeDhcp4 protocol interface.
+ //
+ EFI_PXE_DHCP4_PROTOCOL PxeDhcp4;
+
+ //
+ // Consumed PxeBc, Snp and PxeDhcp4Callback protocol interfaces.
+ //
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_PXE_DHCP4_CALLBACK_PROTOCOL *callback;
+
+ //
+ // PxeDhcp4 called function for PxeDhcp4Callback.
+ //
+ EFI_PXE_DHCP4_FUNCTION function;
+
+ //
+ // Timeout event and flag for PxeDhcp4Callback.
+ //
+ EFI_EVENT TimeoutEvent;
+ BOOLEAN TimeoutOccurred;
+
+ //
+ // Periodic event and flag for PxeDhcp4Callback.
+ //
+ EFI_EVENT PeriodicEvent;
+ BOOLEAN PeriodicOccurred;
+
+ //
+ // DHCP server IP address.
+ //
+ UINT32 ServerIp;
+
+ //
+ // DHCP renewal and rebinding times, in seconds.
+ //
+ UINT32 RenewTime;
+ UINT32 RebindTime;
+ UINT32 LeaseTime;
+
+ //
+ // Number of offers received & allocated offer list.
+ //
+ UINTN offers;
+ DHCP4_PACKET *offer_list;
+
+ //
+ //
+ //
+ BOOLEAN StopPxeBc;
+
+} PXE_DHCP4_PRIVATE_DATA;
+
+#define PXE_DHCP4_PRIVATE_DATA_FROM_THIS(a) CR (a, PXE_DHCP4_PRIVATE_DATA, PxeDhcp4, PXE_DHCP4_PRIVATE_DATA_SIGNATURE)
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// Protocol function prototypes.
+//
+extern
+EFI_STATUS
+EFIAPI
+PxeDhcp4Run (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN OPTIONAL UINTN OpLen,
+ IN OPTIONAL VOID *OpList
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+PxeDhcp4Setup (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN EFI_PXE_DHCP4_DATA *Data
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+PxeDhcp4Init (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN seconds_timeout,
+ OUT UINTN *offer_list_entries,
+ OUT DHCP4_PACKET **offer_list
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+PxeDhcp4Select (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN seconds_timeout,
+ IN DHCP4_PACKET *offer_list
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+PxeDhcp4Renew (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ UINTN seconds_timeout
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+PxeDhcp4Rebind (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ UINTN seconds_timeout
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+PxeDhcp4Release (
+ IN EFI_PXE_DHCP4_PROTOCOL *This
+ )
+;
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// Support function prototypes.
+//
+extern
+UINT16
+htons (
+ UINTN n
+ )
+;
+
+extern
+UINT32
+htonl (
+ UINTN n
+ )
+;
+
+extern
+VOID
+EFIAPI
+timeout_notify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+;
+
+extern
+VOID
+EFIAPI
+periodic_notify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+;
+
+extern
+EFI_STATUS
+find_opt (
+ IN DHCP4_PACKET *Packet,
+ IN UINT8 OpCode,
+ IN UINTN Skip,
+ OUT DHCP4_OP **OpPtr
+ )
+;
+
+extern
+EFI_STATUS
+add_opt (
+ IN DHCP4_PACKET *Packet,
+ IN DHCP4_OP *OpPtr
+ )
+;
+
+extern
+EFI_STATUS
+start_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN OPTIONAL EFI_IP_ADDRESS *station_ip,
+ IN OPTIONAL EFI_IP_ADDRESS *subnet_mask
+ )
+;
+
+extern
+VOID
+stop_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private
+ )
+;
+
+extern
+EFI_STATUS
+start_receive_events (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN UINTN seconds_timeout
+ )
+;
+
+extern
+VOID
+stop_receive_events (
+ IN PXE_DHCP4_PRIVATE_DATA *Private
+ )
+;
+
+extern
+EFI_STATUS
+tx_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN EFI_IP_ADDRESS *dest_ip,
+ IN OPTIONAL EFI_IP_ADDRESS *gateway_ip,
+ IN EFI_IP_ADDRESS *src_ip,
+ IN VOID *buffer,
+ IN UINTN BufferSize
+ )
+;
+
+extern
+EFI_STATUS
+rx_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ OUT VOID *buffer,
+ OUT UINTN *BufferSize,
+ IN OUT EFI_IP_ADDRESS *dest_ip,
+ IN OUT EFI_IP_ADDRESS *src_ip,
+ IN UINT16 op_flags
+ )
+;
+
+extern
+EFI_STATUS
+tx_rx_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN OUT EFI_IP_ADDRESS *ServerIp,
+ IN OPTIONAL EFI_IP_ADDRESS *gateway_ip,
+ IN OPTIONAL EFI_IP_ADDRESS *client_ip,
+ IN OPTIONAL EFI_IP_ADDRESS *subnet_mask,
+ IN DHCP4_PACKET *tx_pkt,
+ OUT DHCP4_PACKET *rx_pkt,
+ IN INTN
+ (
+ *rx_vfy)
+ (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN DHCP4_PACKET *tx_pkt,
+ IN DHCP4_PACKET *rx_pkt,
+ IN UINTN rx_pkt_size
+ ),
+ IN UINTN seconds_timeout
+ )
+;
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+//
+// Global variable definitions.
+//
+extern EFI_COMPONENT_NAME_PROTOCOL gPxeDhcp4ComponentName;
+
+EFI_STATUS
+EFIAPI
+PxeDhcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+ Register Driver Binding protocol for this driver.
+
+Arguments:
+ (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT)
+
+Returns:
+ EFI_SUCCESS - Driver loaded.
+ other - Driver not loaded.
+
+--*/
+;
+
+#ifdef EFI_SIZE_REDUCTION_APPLIED
+ #define COMPONENT_NAME_CODE(code)
+ #define COMPONENT_NAME NULL
+#else
+ #define COMPONENT_NAME_CODE(code) code
+ #define COMPONENT_NAME &gPxeDhcp4ComponentName
+#endif
+
+#endif /* _PXEDHCP4_H */
+
+/* EOF - PxeDhcp4.h */
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf
new file mode 100644
index 0000000000..70daa05cc5
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf
@@ -0,0 +1,64 @@
+#/** @file
+# Component name for module PxeDhcp4
+#
+# Copyright (c) 2007, Intel Corporation
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PxeDhcp4Dxe
+ FILE_GUID = a46c3330-be36-4977-9d24-a7cf92eef0fe
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = PxeDhcp4DriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ support.c
+ PxeDhcp4Release.c
+ PxeDhcp4Setup.c
+ ComponentName.c
+ PxeDhcp4RenewRebind.c
+ PxeDhcp4.h
+ PxeDhcp4.c
+ PxeDhcp4InitSelect.c
+ PxeDhcp4Run.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+
+
+[Protocols]
+ gEfiPxeBaseCodeProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiPxeDhcp4CallbackProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiPxeDhcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa
new file mode 100644
index 0000000000..5db7cf42c3
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa
@@ -0,0 +1,78 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>PxeDhcp4</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>a46c3330-be36-4977-9d24-a7cf92eef0fe</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module PxeDhcp4</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>PxeDhcp4</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseMemoryLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>PxeDhcp4Run.c</Filename>
+ <Filename>PxeDhcp4InitSelect.c</Filename>
+ <Filename>PxeDhcp4Entry.c</Filename>
+ <Filename>PxeDhcp4.c</Filename>
+ <Filename>PxeDhcp4.h</Filename>
+ <Filename>PxeDhcp4RenewRebind.c</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>PxeDhcp4Setup.c</Filename>
+ <Filename>PxeDhcp4Release.c</Filename>
+ <Filename>support.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiPxeDhcp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiPxeDhcp4CallbackProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiSimpleNetworkProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiPxeBaseCodeProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>PxeDhcp4DriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c
new file mode 100644
index 0000000000..b7849d0a87
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c
@@ -0,0 +1,784 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4InitSelect.c
+
+Abstract:
+
+
+**/
+
+
+#include "PxeDhcp4.h"
+
+#define DebugPrint(x)
+//
+// #define DebugPrint(x) Aprint x
+//
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+**/
+STATIC
+INTN
+offer_verify (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN DHCP4_PACKET *tx_pkt,
+ IN DHCP4_PACKET *rx_pkt,
+ IN UINTN rx_pkt_size
+ )
+{
+ EFI_STATUS EfiStatus;
+ DHCP4_PACKET *tmp;
+ DHCP4_OP *msg_type_op;
+ DHCP4_OP *srvid_op;
+ UINT32 magik;
+
+ //
+ // Verify parameters. Touch unused parameters to keep
+ // compiler happy.
+ //
+ ASSERT (Private);
+ ASSERT (rx_pkt);
+
+ if (Private == NULL || rx_pkt == NULL) {
+ return -2;
+ }
+
+ tx_pkt = tx_pkt;
+ rx_pkt_size = rx_pkt_size;
+
+ //
+ // This may be a BOOTP Reply or DHCP Offer packet.
+ // If there is no DHCP magik number, assume that
+ // this is a BOOTP Reply packet.
+ //
+ magik = htonl (DHCP4_MAGIK_NUMBER);
+
+ while (!CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {
+ //
+ // If there is no DHCP message type option, assume
+ // this is a BOOTP reply packet and cache it.
+ //
+ EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ break;
+ }
+ //
+ // If there is a DHCP message type option, it must be a
+ // DHCP offer packet
+ //
+ if (msg_type_op->len != 1) {
+ return -1;
+ }
+
+ if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) {
+ return -1;
+ }
+ //
+ // There must be a server identifier option.
+ //
+ EfiStatus = find_opt (
+ rx_pkt,
+ DHCP4_SERVER_IDENTIFIER,
+ 0,
+ &srvid_op
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ return -1;
+ }
+
+ if (srvid_op->len != 4) {
+ return -1;
+ }
+ //
+ // Good DHCP offer packet.
+ //
+ break;
+ }
+ //
+ // Good DHCP (or BOOTP) packet. Cache it!
+ //
+ EfiStatus = gBS->AllocatePool (
+ EfiBootServicesData,
+ (Private->offers + 1) * sizeof (DHCP4_PACKET),
+ (VOID **) &tmp
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ return -2;
+ }
+
+ ASSERT (tmp);
+
+ if (Private->offers != 0) {
+ CopyMem (
+ tmp,
+ Private->offer_list,
+ Private->offers * sizeof (DHCP4_PACKET)
+ );
+
+ gBS->FreePool (Private->offer_list);
+ }
+
+ CopyMem (&tmp[Private->offers++], rx_pkt, sizeof (DHCP4_PACKET));
+
+ Private->offer_list = tmp;
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+**/
+STATIC
+INTN
+acknak_verify (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN DHCP4_PACKET *tx_pkt,
+ IN DHCP4_PACKET *rx_pkt,
+ IN UINTN rx_pkt_size
+ )
+{
+ EFI_STATUS EfiStatus;
+ DHCP4_OP *msg_type_op;
+ DHCP4_OP *srvid_op;
+ DHCP4_OP *renew_op;
+ DHCP4_OP *rebind_op;
+ DHCP4_OP *lease_time_op;
+ UINT32 magik;
+
+ //
+ // Verify parameters. Touch unused parameters to
+ // keep compiler happy.
+ //
+ ASSERT (Private);
+ ASSERT (rx_pkt);
+
+ if (Private == NULL || rx_pkt == NULL) {
+ return -2;
+ }
+
+ tx_pkt = tx_pkt;
+ rx_pkt_size = rx_pkt_size;
+
+ //
+ // This must be a DHCP Ack message.
+ //
+ magik = htonl (DHCP4_MAGIK_NUMBER);
+
+ if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {
+ return -1;
+ }
+
+ EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ return -1;
+ }
+
+ if (msg_type_op->len != 1) {
+ return -1;
+ }
+
+ if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) {
+ return -1;
+ }
+ //
+ // There must be a server identifier.
+ //
+ EfiStatus = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ return -1;
+ }
+
+ if (srvid_op->len != 4) {
+ return -1;
+ }
+ //
+ // There should be a renewal time.
+ // If there is not, we will default to the 7/8 of the rebinding time.
+ //
+ EfiStatus = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ renew_op = NULL;
+ } else if (renew_op->len != 4) {
+ renew_op = NULL;
+ }
+ //
+ // There should be a rebinding time.
+ // If there is not, we will default to 7/8 of the lease time.
+ //
+ EfiStatus = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ rebind_op = NULL;
+ } else if (rebind_op->len != 4) {
+ rebind_op = NULL;
+ }
+ //
+ // There should be a lease time.
+ // If there is not, we will default to one week.
+ //
+ EfiStatus = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ lease_time_op = NULL;
+ } else if (lease_time_op->len != 4) {
+ lease_time_op = NULL;
+ }
+ //
+ // Packet looks good. Double check the renew, rebind and lease times.
+ //
+ CopyMem (&Private->ServerIp, srvid_op->data, 4);
+
+ if (renew_op != NULL) {
+ CopyMem (&Private->RenewTime, renew_op->data, 4);
+ Private->RenewTime = htonl (Private->RenewTime);
+ } else {
+ Private->RenewTime = 0;
+ }
+
+ if (rebind_op != NULL) {
+ CopyMem (&Private->RebindTime, rebind_op->data, 4);
+ Private->RebindTime = htonl (Private->RebindTime);
+ } else {
+ Private->RebindTime = 0;
+ }
+
+ if (lease_time_op != NULL) {
+ CopyMem (&Private->LeaseTime, lease_time_op->data, 4);
+ Private->LeaseTime = htonl (Private->LeaseTime);
+ } else {
+ Private->LeaseTime = 0;
+ }
+
+ if (Private->LeaseTime < 60) {
+ Private->LeaseTime = 7 * 86400;
+ }
+
+ if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) {
+ Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8;
+ }
+
+ if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) {
+ Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8;
+ }
+
+ return 1;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+EFIAPI
+PxeDhcp4Init (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN seconds_timeout,
+ OUT UINTN *Offers,
+ OUT DHCP4_PACKET **OfferList
+ )
+{
+ PXE_DHCP4_PRIVATE_DATA *Private;
+ DHCP4_PACKET offer;
+ EFI_IP_ADDRESS bcast_ip;
+ EFI_STATUS EfiStatus;
+
+ //
+ // Verify parameters and protocol state.
+ //
+ if (This == NULL ||
+ seconds_timeout < DHCP4_MIN_SECONDS ||
+ seconds_timeout > DHCP4_MAX_SECONDS ||
+ Offers == NULL ||
+ OfferList == NULL
+ ) {
+ //
+ // Return parameters are not initialized when
+ // parameters are invalid!
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Offers = 0;
+ *OfferList = NULL;
+
+ //
+ // Check protocol state.
+ //
+ if (This->Data == NULL) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!This->Data->SetupCompleted) {
+ return EFI_NOT_READY;
+ }
+
+#if 0
+ if (!is_good_discover (&This->Data->Discover)) {
+ //
+ // %%TBD - check discover packet fields
+ //
+ }
+#endif
+ //
+ // Get pointer to our instance data.
+ //
+ Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Private->PxeBc == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Setup variables...
+ //
+ Private->offers = 0;
+ Private->offer_list = NULL;
+
+ EfiStatus = gBS->HandleProtocol (
+ Private->Handle,
+ &gEfiPxeDhcp4CallbackProtocolGuid,
+ (VOID *) &Private->callback
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ Private->callback = NULL;
+ }
+
+ Private->function = EFI_PXE_DHCP4_FUNCTION_INIT;
+
+ //
+ // Increment the transaction ID.
+ //
+ {
+ UINT32 xid;
+
+ CopyMem (&xid, &This->Data->Discover.dhcp4.xid, sizeof (UINT32));
+
+ xid = htonl (htonl (xid) + 1);
+
+ CopyMem (&This->Data->Discover.dhcp4.xid, &xid, sizeof (UINT32));
+ }
+ //
+ // Transmit discover and wait for offers...
+ //
+ SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
+
+ EfiStatus = tx_rx_udp (
+ Private,
+ &bcast_ip,
+ NULL,
+ NULL,
+ NULL,
+ &This->Data->Discover,
+ &offer,
+ &offer_verify,
+ seconds_timeout
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ if (Private->offer_list) {
+ gBS->FreePool (Private->offer_list);
+ }
+
+ Private->offers = 0;
+ Private->offer_list = NULL;
+ Private->callback = NULL;
+
+ DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
+ return EfiStatus;
+ }
+
+ *Offers = Private->offers;
+ *OfferList = Private->offer_list;
+
+ Private->offers = 0;
+ Private->offer_list = NULL;
+ Private->callback = NULL;
+
+ This->Data->InitCompleted = TRUE;
+ This->Data->SelectCompleted = FALSE;
+ This->Data->IsBootp = FALSE;
+ This->Data->IsAck = FALSE;
+
+ return EFI_SUCCESS;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+EFIAPI
+PxeDhcp4Select (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN seconds_timeout,
+ IN DHCP4_PACKET *Offer
+ )
+{
+ PXE_DHCP4_PRIVATE_DATA *Private;
+ EFI_STATUS EfiStatus;
+ DHCP4_PACKET request;
+ DHCP4_PACKET acknak;
+ EFI_IP_ADDRESS bcast_ip;
+ EFI_IP_ADDRESS zero_ip;
+ EFI_IP_ADDRESS local_ip;
+ DHCP4_OP *srvid;
+ DHCP4_OP *op;
+ UINT32 dhcp4_magik;
+ UINT8 buf[16];
+ BOOLEAN is_bootp;
+
+ //
+ // Verify parameters.
+ //
+ if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS || Offer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Check protocol state.
+ //
+ if (This->Data == NULL) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!This->Data->SetupCompleted) {
+ return EFI_NOT_READY;
+ }
+ //
+ // Get pointer to instance data.
+ //
+ Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Private->PxeBc == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+#if 0
+ if (!is_good_discover (&This->Data->Discover)) {
+ //
+ // %%TBD - check discover packet fields
+ //
+ }
+#endif
+ //
+ // Setup useful variables...
+ //
+ SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
+
+ ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));
+
+ ZeroMem (&local_ip, sizeof (EFI_IP_ADDRESS));
+ local_ip.v4.Addr[0] = 127;
+ local_ip.v4.Addr[3] = 1;
+
+ This->Data->SelectCompleted = FALSE;
+ This->Data->IsBootp = FALSE;
+ This->Data->IsAck = FALSE;
+
+ EfiStatus = gBS->HandleProtocol (
+ Private->Handle,
+ &gEfiPxeDhcp4CallbackProtocolGuid,
+ (VOID *) &Private->callback
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ Private->callback = NULL;
+ }
+
+ Private->function = EFI_PXE_DHCP4_FUNCTION_SELECT;
+
+ //
+ // Verify offer packet fields.
+ //
+ if (Offer->dhcp4.op != BOOTP_REPLY) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Offer->dhcp4.htype != This->Data->Discover.dhcp4.htype) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Offer->dhcp4.hlen != This->Data->Discover.dhcp4.hlen) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (CompareMem (&Offer->dhcp4.xid, &This->Data->Discover.dhcp4.xid, 4)) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!CompareMem (&Offer->dhcp4.yiaddr, &bcast_ip, 4)) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!CompareMem (&Offer->dhcp4.yiaddr, &zero_ip, 4)) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!CompareMem (&Offer->dhcp4.yiaddr, &local_ip, 4)) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (CompareMem (
+ &Offer->dhcp4.chaddr,
+ &This->Data->Discover.dhcp4.chaddr,
+ 16
+ )) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // DHCP option checks
+ //
+ dhcp4_magik = htonl (DHCP4_MAGIK_NUMBER);
+ is_bootp = TRUE;
+
+ if (!CompareMem (&Offer->dhcp4.magik, &dhcp4_magik, 4)) {
+ //
+ // If present, DHCP message type must be offer.
+ //
+ EfiStatus = find_opt (Offer, DHCP4_MESSAGE_TYPE, 0, &op);
+
+ if (!EFI_ERROR (EfiStatus)) {
+ if (op->len != 1 || op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ is_bootp = FALSE;
+ }
+ //
+ // If present, DHCP max message size must be valid.
+ //
+ EfiStatus = find_opt (Offer, DHCP4_MAX_MESSAGE_SIZE, 0, &op);
+
+ if (!EFI_ERROR (EfiStatus)) {
+ if (op->len != 2 || ((op->data[0] << 8) | op->data[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // If present, DHCP server identifier must be valid.
+ //
+ EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &op);
+
+ if (!EFI_ERROR (EfiStatus)) {
+ if (op->len != 4 || !CompareMem (op->data, &bcast_ip, 4) || !CompareMem (op->data, &zero_ip, 4)) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // If present, DHCP subnet mask must be valid.
+ //
+ EfiStatus = find_opt (
+ Offer,
+ DHCP4_SUBNET_MASK,
+ 0,
+ &op
+ );
+
+ if (!EFI_ERROR (EfiStatus)) {
+ if (op->len != 4) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+ //
+ // Early out for BOOTP.
+ //
+ This->Data->IsBootp = is_bootp;
+ if (is_bootp) {
+ //
+ // Copy offer packet to instance data.
+ //
+ CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET));
+
+ //
+ // Copy discover to request and offer to acknak.
+ //
+ CopyMem (
+ &This->Data->Request,
+ &This->Data->Discover,
+ sizeof (DHCP4_PACKET)
+ );
+
+ CopyMem (
+ &This->Data->AckNak,
+ &This->Data->Offer,
+ sizeof (DHCP4_PACKET)
+ );
+
+ //
+ // Set state flags.
+ //
+ This->Data->SelectCompleted = TRUE;
+ This->Data->IsAck = TRUE;
+
+ Private->callback = NULL;
+ return EFI_SUCCESS;
+ }
+ //
+ // Copy discover packet contents to request packet.
+ //
+ CopyMem (&request, &This->Data->Discover, sizeof (DHCP4_PACKET));
+
+ This->Data->IsAck = FALSE;
+
+ //
+ // Change DHCP message type from discover to request.
+ //
+ EfiStatus = find_opt (&request, DHCP4_MESSAGE_TYPE, 0, &op);
+
+ if (EFI_ERROR (EfiStatus) && EfiStatus != EFI_NOT_FOUND) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (EfiStatus == EFI_NOT_FOUND) {
+ EfiStatus = find_opt (&request, DHCP4_END, 0, &op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ op->op = DHCP4_MESSAGE_TYPE;
+ op->len = 1;
+
+ op->data[1] = DHCP4_END;
+ }
+
+ op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST;
+
+ //
+ // Copy server identifier option from offer to request.
+ //
+ EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &srvid);
+
+ if (EFI_ERROR (EfiStatus)) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (srvid->len != 4) {
+ Private->callback = NULL;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiStatus = add_opt (&request, srvid);
+
+ if (EFI_ERROR (EfiStatus)) {
+ DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
+ Private->callback = NULL;
+ return EfiStatus;
+ }
+ //
+ // Add requested IP address option to request packet.
+ //
+ op = (DHCP4_OP *) buf;
+ op->op = DHCP4_REQUESTED_IP_ADDRESS;
+ op->len = 4;
+ CopyMem (op->data, &Offer->dhcp4.yiaddr, 4);
+
+ EfiStatus = add_opt (&request, op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
+ Private->callback = NULL;
+ return EfiStatus;
+ }
+ //
+ // Transimit DHCP request and wait for DHCP ack...
+ //
+ SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
+
+ EfiStatus = tx_rx_udp (
+ Private,
+ &bcast_ip,
+ NULL,
+ NULL,
+ NULL,
+ &request,
+ &acknak,
+ &acknak_verify,
+ seconds_timeout
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
+ Private->callback = NULL;
+ return EfiStatus;
+ }
+ //
+ // Set Data->IsAck and return.
+ //
+ EfiStatus = find_opt (&acknak, DHCP4_MESSAGE_TYPE, 0, &op);
+
+ if (EFI_ERROR (EfiStatus)) {
+ Private->callback = NULL;
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (op->len != 1) {
+ Private->callback = NULL;
+ return EFI_DEVICE_ERROR;
+ }
+
+ switch (op->data[0]) {
+ case DHCP4_MESSAGE_TYPE_ACK:
+ This->Data->IsAck = TRUE;
+ break;
+
+ case DHCP4_MESSAGE_TYPE_NAK:
+ This->Data->IsAck = FALSE;
+ break;
+
+ default:
+ Private->callback = NULL;
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Copy packets into instance data...
+ //
+ CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET));
+ CopyMem (&This->Data->Request, &request, sizeof (DHCP4_PACKET));
+ CopyMem (&This->Data->AckNak, &acknak, sizeof (DHCP4_PACKET));
+
+ This->Data->SelectCompleted = TRUE;
+
+ Private->callback = NULL;
+ return EFI_SUCCESS;
+}
+
+/* eof - PxeDhcp4InitSelect.c */
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c
new file mode 100644
index 0000000000..119acffceb
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c
@@ -0,0 +1,247 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4Release.c
+
+Abstract:
+ Transmit release packet, free allocations and shutdown PxeDhcp4.
+
+
+**/
+
+
+#include "PxeDhcp4.h"
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+EFIAPI
+PxeDhcp4Release (
+ IN EFI_PXE_DHCP4_PROTOCOL *This
+ )
+{
+ PXE_DHCP4_PRIVATE_DATA *Private;
+ EFI_IP_ADDRESS ServerIp;
+ EFI_IP_ADDRESS client_ip;
+ EFI_IP_ADDRESS gateway_ip;
+ EFI_IP_ADDRESS subnet_mask;
+ EFI_STATUS efi_status;
+ DHCP4_OP *op;
+ UINT8 op_list[20];
+
+ //
+ // Check for invalid parameters.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Release does nothing if the protocol has never been setup.
+ //
+ if (This->Data == NULL) {
+ return EFI_NOT_STARTED;
+ }
+ //
+ // Fail if we do not have valid instance data.
+ //
+ Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Private->PxeBc == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // If this is a BOOTP session and there is not a DHCP Ack
+ // packet, just release storage and return.
+ //
+ if (This->Data->IsBootp || !This->Data->IsAck) {
+ gBS->FreePool (This->Data);
+ This->Data = NULL;
+
+ if (Private->StopPxeBc) {
+ Private->PxeBc->Stop (Private->PxeBc);
+ }
+
+ return EFI_SUCCESS;
+ }
+ //
+ // Build option list for DHCP Release packet.
+ // If any errors occur, just release storage and return.
+ //
+ //
+ // Message type is first.
+ //
+ op_list[0] = DHCP4_MESSAGE_TYPE;
+ op_list[1] = 1;
+ op_list[2] = DHCP4_MESSAGE_TYPE_RELEASE;
+
+ //
+ // Followed by server identifier.
+ //
+ efi_status = find_opt (
+ &This->Data->Request,
+ DHCP4_SERVER_IDENTIFIER,
+ 0,
+ &op
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ gBS->FreePool (This->Data);
+ This->Data = NULL;
+
+ if (Private->StopPxeBc) {
+ Private->PxeBc->Stop (Private->PxeBc);
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ if (op->len != 4) {
+ gBS->FreePool (This->Data);
+ This->Data = NULL;
+
+ if (Private->StopPxeBc) {
+ Private->PxeBc->Stop (Private->PxeBc);
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ CopyMem (&ServerIp, op->data, 4);
+
+ op_list[3] = DHCP4_SERVER_IDENTIFIER;
+ op_list[4] = 4;
+ CopyMem (&op_list[5], &ServerIp, 4);
+
+ //
+ // Followed by end.
+ //
+ op_list[9] = DHCP4_END;
+
+ //
+ // We need a subnet mask for IP stack operation.
+ //
+ efi_status = find_opt (
+ &This->Data->AckNak,
+ DHCP4_SUBNET_MASK,
+ 0,
+ &op
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ gBS->FreePool (This->Data);
+ This->Data = NULL;
+
+ if (Private->StopPxeBc) {
+ Private->PxeBc->Stop (Private->PxeBc);
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ if (op->len != 4) {
+ gBS->FreePool (This->Data);
+ This->Data = NULL;
+
+ if (Private->StopPxeBc) {
+ Private->PxeBc->Stop (Private->PxeBc);
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ ZeroMem (&subnet_mask, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&subnet_mask, op->data, 4);
+
+ //
+ // Gateway IP address may be needed.
+ //
+ ZeroMem (&gateway_ip, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&gateway_ip, &This->Data->AckNak.dhcp4.giaddr, 4);
+
+ //
+ // Client IP address needed for IP stack operation.
+ //
+ ZeroMem (&client_ip, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&client_ip, &This->Data->AckNak.dhcp4.yiaddr, 4);
+
+ //
+ // Enable UDP...
+ //
+ efi_status = start_udp (Private, &client_ip, &subnet_mask);
+
+ if (EFI_ERROR (efi_status)) {
+ gBS->FreePool (This->Data);
+ This->Data = NULL;
+
+ if (Private->StopPxeBc) {
+ Private->PxeBc->Stop (Private->PxeBc);
+ }
+
+ return efi_status;
+ }
+ //
+ // Gather information out of DHCP request packet needed for
+ // DHCP release packet.
+ //
+ //
+ // Setup DHCP Release packet.
+ //
+ CopyMem (&This->Data->Request.dhcp4.ciaddr, &client_ip, 4);
+
+ ZeroMem (&This->Data->Request.dhcp4.yiaddr, 12);
+
+ ZeroMem (&This->Data->Request.dhcp4.sname, 64 + 128);
+
+ This->Data->Request.dhcp4.hops = 0;
+ This->Data->Request.dhcp4.secs = 0;
+ This->Data->Request.dhcp4.flags = 0;
+
+ ZeroMem (
+ &This->Data->Request.dhcp4.options,
+ sizeof This->Data->Request.dhcp4.options
+ );
+
+ CopyMem (&This->Data->Request.dhcp4.options, op_list, 10);
+
+ //
+ // Transmit DHCP Release packet.
+ //
+ tx_udp (
+ Private,
+ &ServerIp,
+ &gateway_ip,
+ &client_ip,
+ &This->Data->Request,
+ DHCP4_MAX_PACKET_SIZE - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE)
+ );
+
+ gBS->Stall (1000000); /* 1/10th second */
+
+ //
+ // Shutdown PXE BaseCode and release local storage.
+ //
+ stop_udp (Private);
+
+ gBS->FreePool (This->Data);
+ This->Data = NULL;
+
+ if (Private->StopPxeBc) {
+ Private->PxeBc->Stop (Private->PxeBc);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/* eof - PxeDhcp4Release.c */
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c
new file mode 100644
index 0000000000..193bb1880a
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c
@@ -0,0 +1,408 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4RenewRebind.c
+
+Abstract:
+
+
+**/
+
+
+#include "PxeDhcp4.h"
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+ Parameters:
+
+ @return -2 = ignore, stop waiting
+ @return -1 = ignore, keep waiting
+ @return 0 = accept, keep waiting
+ @return 1 = accept, stop waiting
+
+**/
+STATIC
+INTN
+acknak_verify (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN DHCP4_PACKET *tx_pkt,
+ IN DHCP4_PACKET *rx_pkt,
+ IN UINTN rx_pkt_size
+ )
+{
+ EFI_STATUS efi_status;
+ DHCP4_OP *msg_type_op;
+ DHCP4_OP *srvid_op;
+ DHCP4_OP *renew_op;
+ DHCP4_OP *rebind_op;
+ DHCP4_OP *lease_time_op;
+ UINT32 magik;
+
+ //
+ // Verify parameters. Unused parameters are also touched
+ // to make the compiler happy.
+ //
+ ASSERT (Private);
+ ASSERT (rx_pkt);
+
+ if (Private == NULL || rx_pkt == NULL) {
+ return -2;
+ }
+
+ tx_pkt = tx_pkt;
+ rx_pkt_size = rx_pkt_size;
+
+ //
+ // This must be a DHCP Ack message.
+ //
+ magik = htonl (DHCP4_MAGIK_NUMBER);
+
+ if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {
+ return -1;
+ }
+
+ efi_status = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);
+
+ if (EFI_ERROR (efi_status)) {
+ return -1;
+ }
+
+ if (msg_type_op->len != 1) {
+ return -1;
+ }
+
+ if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) {
+ return -1;
+ }
+ //
+ // There must be a server identifier.
+ //
+ efi_status = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op);
+
+ if (EFI_ERROR (efi_status)) {
+ return -1;
+ }
+
+ if (srvid_op->len != 4) {
+ return -1;
+ }
+ //
+ // There should be a renewal time.
+ // If there is not, we will default to the 7/8 of the rebinding time.
+ //
+ efi_status = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op);
+
+ if (EFI_ERROR (efi_status)) {
+ renew_op = NULL;
+ } else if (renew_op->len != 4) {
+ renew_op = NULL;
+ }
+ //
+ // There should be a rebinding time.
+ // If there is not, we will default to 7/8 of the lease time.
+ //
+ efi_status = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op);
+
+ if (EFI_ERROR (efi_status)) {
+ rebind_op = NULL;
+ } else if (rebind_op->len != 4) {
+ rebind_op = NULL;
+ }
+ //
+ // There should be a lease time.
+ // If there is not, we will default to one week.
+ //
+ efi_status = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op);
+
+ if (EFI_ERROR (efi_status)) {
+ lease_time_op = NULL;
+ } else if (lease_time_op->len != 4) {
+ lease_time_op = NULL;
+ }
+ //
+ // Packet looks good. Double check the renew, rebind and lease times.
+ //
+ CopyMem (&Private->ServerIp, srvid_op->data, 4);
+
+ if (renew_op != NULL) {
+ CopyMem (&Private->RenewTime, renew_op->data, 4);
+ Private->RenewTime = htonl (Private->RenewTime);
+ } else {
+ Private->RenewTime = 0;
+ }
+
+ if (rebind_op != NULL) {
+ CopyMem (&Private->RebindTime, rebind_op->data, 4);
+ Private->RebindTime = htonl (Private->RebindTime);
+ } else {
+ Private->RebindTime = 0;
+ }
+
+ if (lease_time_op != NULL) {
+ CopyMem (&Private->LeaseTime, lease_time_op->data, 4);
+ Private->LeaseTime = htonl (Private->LeaseTime);
+ } else {
+ Private->LeaseTime = 0;
+ }
+
+ if (Private->LeaseTime < 60) {
+ Private->LeaseTime = 7 * 86400;
+ }
+
+ if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) {
+ Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8;
+ }
+
+ if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) {
+ Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8;
+ }
+
+ return 1;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+STATIC
+EFI_STATUS
+EFIAPI
+renew_rebind (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN seconds_timeout,
+ IN BOOLEAN renew
+ )
+{
+ PXE_DHCP4_PRIVATE_DATA *Private;
+ EFI_IP_ADDRESS ServerIp;
+ EFI_IP_ADDRESS client_ip;
+ EFI_IP_ADDRESS subnet_mask;
+ EFI_IP_ADDRESS gateway_ip;
+ DHCP4_PACKET Request;
+ DHCP4_PACKET AckNak;
+ DHCP4_OP *op;
+ EFI_STATUS efi_status;
+
+ //
+ // Check for invalid parameters.
+ //
+ if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Check for proper protocol state.
+ //
+ if (This->Data == NULL) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!This->Data->SelectCompleted) {
+ return EFI_NOT_READY;
+ }
+
+ if (This->Data->IsBootp) {
+ return EFI_SUCCESS;
+ }
+
+ if (!This->Data->IsAck) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Get pointer to instance data.
+ //
+ Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Private->PxeBc == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Copy Discover packet to temporary request packet
+ // to be used for Renew/Rebind operation.
+ //
+ CopyMem (&Request, &This->Data->Discover, sizeof (DHCP4_PACKET));
+
+ CopyMem (&Request.dhcp4.ciaddr, &This->Data->AckNak.dhcp4.yiaddr, 4);
+
+ Request.dhcp4.flags = 0; /* Reply does not need to be broadcast. */
+
+ //
+ // Change message type from discover to request.
+ //
+ efi_status = find_opt (&Request, DHCP4_MESSAGE_TYPE, 0, &op);
+
+ if (EFI_ERROR (efi_status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (op->len != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST;
+
+ //
+ // Need a subnet mask.
+ //
+ efi_status = find_opt (
+ &This->Data->AckNak,
+ DHCP4_SUBNET_MASK,
+ 0,
+ &op
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (op->len != 4) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&subnet_mask, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&subnet_mask, op->data, 4);
+
+ //
+ // Need a server IP address (renew) or a broadcast
+ // IP address (rebind).
+ //
+ ZeroMem (&gateway_ip, sizeof (EFI_IP_ADDRESS));
+
+ if (renew) {
+ efi_status = find_opt (
+ &This->Data->AckNak,
+ DHCP4_SERVER_IDENTIFIER,
+ 0,
+ &op
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (op->len != 4) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&ServerIp, op->data, 4);
+
+ //
+ //
+ //
+ if (CompareMem (&This->Data->AckNak.dhcp4.giaddr, &gateway_ip, 4)) {
+ CopyMem (&gateway_ip, &This->Data->AckNak.dhcp4.giaddr, 4);
+ }
+ } else {
+ SetMem (&ServerIp, sizeof (EFI_IP_ADDRESS), 0xFF);
+ }
+ //
+ // Need a client IP address.
+ //
+ ZeroMem (&client_ip, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&client_ip, &Request.dhcp4.ciaddr, 4);
+
+ //
+ //
+ //
+ efi_status = gBS->HandleProtocol (
+ Private->Handle,
+ &gEfiPxeDhcp4CallbackProtocolGuid,
+ (VOID *) &Private->callback
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ Private->callback = NULL;
+ }
+
+ Private->function = renew ? EFI_PXE_DHCP4_FUNCTION_RENEW : EFI_PXE_DHCP4_FUNCTION_REBIND;
+
+ //
+ // Transimit DHCP request and wait for DHCP ack...
+ //
+ efi_status = tx_rx_udp (
+ Private,
+ &ServerIp,
+ &gateway_ip,
+ &client_ip,
+ &subnet_mask,
+ &Request,
+ &AckNak,
+ &acknak_verify,
+ seconds_timeout
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ Private->callback = NULL;
+ return efi_status;
+ }
+ //
+ // Copy server identifier, renewal time and rebinding time
+ // from temporary ack/nak packet into cached ack/nak packet.
+ //
+ efi_status = find_opt (
+ &This->Data->AckNak,
+ DHCP4_SERVER_IDENTIFIER,
+ 0,
+ &op
+ );
+
+ if (!EFI_ERROR (efi_status)) {
+ if (op->len == 4) {
+ CopyMem (op->data, &Private->ServerIp, 4);
+ }
+ }
+
+ efi_status = find_opt (&This->Data->AckNak, DHCP4_RENEWAL_TIME, 0, &op);
+
+ if (!EFI_ERROR (efi_status)) {
+ if (op->len == 4) {
+ CopyMem (op->data, &Private->RenewTime, 4);
+ }
+ }
+
+ efi_status = find_opt (&This->Data->AckNak, DHCP4_REBINDING_TIME, 0, &op);
+
+ if (!EFI_ERROR (efi_status)) {
+ if (op->len == 4) {
+ CopyMem (op->data, &Private->RebindTime, 4);
+ }
+ }
+
+ Private->callback = NULL;
+ return efi_status;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+EFIAPI
+PxeDhcp4Renew (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN seconds_timeout
+ )
+{
+ return renew_rebind (This, seconds_timeout, TRUE);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+EFIAPI
+PxeDhcp4Rebind (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN UINTN seconds_timeout
+ )
+{
+ return renew_rebind (This, seconds_timeout, FALSE);
+}
+
+/* eof - PxeDhcp4RenewRebind.c */
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c
new file mode 100644
index 0000000000..e796d12676
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c
@@ -0,0 +1,191 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4Run.c
+
+Abstract:
+ Simplified entry point for starting basic PxeDhcp4 client operation.
+
+
+**/
+
+
+#include "PxeDhcp4.h"
+
+EFI_STATUS
+EFIAPI
+PxeDhcp4Run (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN OPTIONAL UINTN OpLen,
+ IN OPTIONAL VOID *OpList
+ )
+{
+ PXE_DHCP4_PRIVATE_DATA *Private;
+ DHCP4_PACKET *offer_list;
+ EFI_STATUS efi_status;
+ EFI_IP_ADDRESS zero_ip;
+ UINTN offers;
+ UINTN timeout;
+ UINTN n;
+ UINT16 seconds;
+
+ //
+ // Validate parameters.
+ //
+ if (This == NULL || (OpLen != 0 && OpList == NULL) || (OpLen == 0 && OpList != NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (n = 0; n < OpLen;) {
+ switch (((UINT8 *) OpList)[n]) {
+ case DHCP4_PAD:
+ ++n;
+ continue;
+
+ case DHCP4_END:
+ ++n;
+ break;
+
+ default:
+ n += 2 + ((UINT8 *) OpList)[n + 1];
+ continue;
+ }
+
+ break;
+ }
+
+ if (n != OpLen) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Get pointer to instance data.
+ //
+ Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Private->PxeBc == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Initialize DHCP discover packet.
+ //
+ efi_status = PxeDhcp4Setup (This, NULL);
+
+ if (EFI_ERROR (efi_status)) {
+ return efi_status;
+ }
+
+ for (n = 0; n < OpLen;) {
+ switch (((UINT8 *) OpList)[n]) {
+ case DHCP4_PAD:
+ ++n;
+ continue;
+
+ case DHCP4_END:
+ ++n;
+ break;
+
+ default:
+ efi_status = add_opt (
+ &This->Data->Discover,
+ (DHCP4_OP *) &(((UINT8 *) OpList)[n])
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ return efi_status;
+ }
+
+ n += 2 + ((UINT8 *) OpList)[n + 1];
+ continue;
+ }
+
+ break;
+ }
+ //
+ // Basic DHCP D.O.R.A.
+ // 1, 2, 4, 8, 16 & 32 second timeouts.
+ // Callback routine can be used to break out earlier.
+ //
+ ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));
+
+ for (timeout = 1;;) {
+ //
+ // Broadcast DHCP discover and wait for DHCP offers.
+ //
+ efi_status = PxeDhcp4Init (This, timeout, &offers, &offer_list);
+
+ if ((efi_status != EFI_SUCCESS) &&
+ (efi_status != EFI_TIMEOUT) &&
+ (efi_status != EFI_NO_RESPONSE)) {
+ return efi_status;
+ }
+ //
+ // Try to select from each DHCP or BOOTP offer.
+ //
+ for (n = 0; n < offers; ++n) {
+ //
+ // Ignore proxyDHCP offers.
+ //
+ if (!CompareMem (&offer_list[n].dhcp4.yiaddr, &zero_ip, 4)) {
+ continue;
+ }
+ //
+ // Issue DHCP Request and wait for DHCP Ack/Nak.
+ //
+ efi_status = PxeDhcp4Select (
+ This,
+ timeout,
+ &offer_list[n]
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ continue;
+ }
+ //
+ // Exit when we have got our DHCP Ack.
+ //
+ if (This->Data->IsAck) {
+ return EFI_SUCCESS;
+ }
+ }
+ //
+ // No DHCP Acks. Release DHCP Offer list storage.
+ //
+ if (offer_list != NULL) {
+ gBS->FreePool (offer_list);
+ offer_list = NULL;
+ }
+ //
+ // Try again until we have used up >= DHCP4_MAX_SECONDS.
+ //
+ if ((timeout <<= 1) > DHCP4_MAX_SECONDS) {
+ if (!EFI_ERROR (efi_status)) {
+ efi_status = EFI_TIMEOUT;
+ }
+
+ return efi_status;
+ }
+ //
+ // Next timeout value.
+ //
+ CopyMem (&seconds, &This->Data->Discover.dhcp4.secs, 2);
+
+ seconds = htons (htons (seconds) + timeout);
+
+ CopyMem (&This->Data->Discover.dhcp4.secs, &seconds, 2);
+ }
+}
+
+/* eof - PxeDhcp4Run.c */
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c
new file mode 100644
index 0000000000..29e4ed05b5
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c
@@ -0,0 +1,258 @@
+/** @file
+
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ PxeDhcp4Setup.c
+
+Abstract:
+
+
+**/
+
+#include "PxeDhcp4.h"
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+EFIAPI
+PxeDhcp4Setup (
+ IN EFI_PXE_DHCP4_PROTOCOL *This,
+ IN EFI_PXE_DHCP4_DATA *Data
+ )
+{
+ PXE_DHCP4_PRIVATE_DATA *Private;
+ DHCP4_HEADER *Packet;
+ EFI_STATUS EfiStatus;
+ UINT8 *OpLen;
+ UINT8 *OpPtr;
+
+ //
+ // Return error if parameters are invalid.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (This->Data != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (Private->PxeBc == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Check contents of provided Data structure.
+ //
+ if (Data != NULL) {
+ //
+ // Do protocol state checks first.
+ //
+ if (Data->SelectCompleted) {
+ if (!Data->InitCompleted || !Data->SetupCompleted) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Data->IsBootp && !Data->IsAck) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else if (Data->InitCompleted) {
+ if (!Data->SetupCompleted || Data->IsBootp || Data->IsAck) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else if (Data->SetupCompleted) {
+ if (Data->IsBootp || Data->IsAck) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // Do packet content checks.
+ //
+ if (Data->SetupCompleted) {
+ //
+ // %%TBD - check discover packet
+ //
+ }
+
+ if (Data->SelectCompleted) {
+ if (Data->IsBootp) {
+ //
+ // %%TBD - check offer packet
+ //
+ if (CompareMem (
+ &Data->Discover,
+ &Data->Request,
+ sizeof (DHCP4_PACKET)
+ )) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (CompareMem (
+ &Data->Offer,
+ &Data->AckNak,
+ sizeof (DHCP4_PACKET)
+ )) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ //
+ // %%TBD - check offer, request & acknak packets
+ //
+ }
+ }
+ }
+ //
+ // Allocate data structure. Return error
+ // if there is not enough available memory.
+ //
+ This->Data = AllocatePool (sizeof (EFI_PXE_DHCP4_DATA));
+ if (This->Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Start PxeBc because we want to use its UdpWrite, UdpRead and
+ // SetFilter calls.
+ //
+ EfiStatus = Private->PxeBc->Start (Private->PxeBc, FALSE);
+
+ if (EFI_ERROR (EfiStatus)) {
+ if (EfiStatus != EFI_ALREADY_STARTED) {
+ FreePool (This->Data);
+ This->Data = NULL;
+ Private->PxeBc->Stop (Private->PxeBc);
+ return EfiStatus;
+ }
+
+ Private->StopPxeBc = FALSE;
+ } else {
+ Private->StopPxeBc = TRUE;
+ }
+ //
+ // Use new data.
+ //
+ if (Data != NULL) {
+ CopyMem (This->Data, Data, sizeof (EFI_PXE_DHCP4_DATA));
+ return EFI_SUCCESS;
+ }
+ //
+ // Initialize new public data structure.
+ //
+ ZeroMem (This->Data, sizeof (EFI_PXE_DHCP4_DATA));
+
+ //
+ // Fill in default DHCP discover packet.
+ // Check for MAC addresses of strange lengths, just in case.
+ //
+ Packet = &This->Data->Discover.dhcp4;
+
+ Packet->op = BOOTP_REQUEST;
+
+ Packet->htype = Private->Snp->Mode->IfType;
+
+ if (Private->Snp->Mode->HwAddressSize > 16) {
+ Packet->hlen = 16;
+ } else {
+ Packet->hlen = (UINT8) Private->Snp->Mode->HwAddressSize;
+ }
+
+ Packet->hops = 0; /* Set to zero per RFC 2131. */
+
+ if (Packet->hlen < sizeof Packet->xid) {
+ if (Packet->hlen != 0) {
+ CopyMem (
+ &Packet->xid,
+ &Private->Snp->Mode->CurrentAddress,
+ Packet->hlen
+ );
+ }
+ } else {
+ CopyMem (
+ &Packet->xid,
+ &Private->Snp->Mode->CurrentAddress.Addr[Packet->hlen - sizeof Packet->xid],
+ sizeof Packet->xid
+ );
+ }
+ //
+ // %%TBD - xid should be randomized
+ //
+ Packet->secs = htons (DHCP4_INITIAL_SECONDS);
+
+ Packet->flags = htons (DHCP4_BROADCAST_FLAG);
+
+ if (Packet->hlen != 0) {
+ CopyMem (Packet->chaddr, &Private->Snp->Mode->CurrentAddress, Packet->hlen);
+ }
+
+ Packet->magik = htonl (DHCP4_MAGIK_NUMBER);
+
+ OpPtr = Packet->options;
+
+ *OpPtr++ = DHCP4_MESSAGE_TYPE;
+ *OpPtr++ = 1;
+ *OpPtr++ = DHCP4_MESSAGE_TYPE_DISCOVER;
+
+ *OpPtr++ = DHCP4_MAX_MESSAGE_SIZE;
+ *OpPtr++ = 2;
+ *OpPtr++ = (UINT8) ((DHCP4_DEFAULT_MAX_MESSAGE_SIZE >> 8) & 0xFF);
+ *OpPtr++ = (UINT8) (DHCP4_DEFAULT_MAX_MESSAGE_SIZE & 0xFF);
+
+ *OpPtr++ = DHCP4_PARAMETER_REQUEST_LIST;
+ OpLen = OpPtr;
+ *OpPtr++ = 0;
+ *OpPtr++ = DHCP4_SUBNET_MASK;
+ *OpPtr++ = DHCP4_TIME_OFFSET;
+ *OpPtr++ = DHCP4_ROUTER_LIST;
+ *OpPtr++ = DHCP4_TIME_SERVERS;
+ *OpPtr++ = DHCP4_NAME_SERVERS;
+ *OpPtr++ = DHCP4_DNS_SERVERS;
+ *OpPtr++ = DHCP4_HOST_NAME;
+ *OpPtr++ = DHCP4_BOOT_FILE_SIZE;
+ *OpPtr++ = DHCP4_MESSAGE_TYPE;
+ *OpPtr++ = DHCP4_DOMAIN_NAME;
+ *OpPtr++ = DHCP4_ROOT_PATH;
+ *OpPtr++ = DHCP4_EXTENSION_PATH;
+ *OpPtr++ = DHCP4_MAX_DATAGRAM_SIZE;
+ *OpPtr++ = DHCP4_DEFAULT_TTL;
+ *OpPtr++ = DHCP4_BROADCAST_ADDRESS;
+ *OpPtr++ = DHCP4_NIS_DOMAIN_NAME;
+ *OpPtr++ = DHCP4_NIS_SERVERS;
+ *OpPtr++ = DHCP4_NTP_SERVERS;
+ *OpPtr++ = DHCP4_VENDOR_SPECIFIC;
+ *OpPtr++ = DHCP4_REQUESTED_IP_ADDRESS;
+ *OpPtr++ = DHCP4_LEASE_TIME;
+ *OpPtr++ = DHCP4_SERVER_IDENTIFIER;
+ *OpPtr++ = DHCP4_RENEWAL_TIME;
+ *OpPtr++ = DHCP4_REBINDING_TIME;
+ *OpPtr++ = DHCP4_CLASS_IDENTIFIER;
+ *OpPtr++ = DHCP4_TFTP_SERVER_NAME;
+ *OpPtr++ = DHCP4_BOOTFILE;
+ *OpPtr++ = 128;
+ *OpPtr++ = 129;
+ *OpPtr++ = 130;
+ *OpPtr++ = 131;
+ *OpPtr++ = 132;
+ *OpPtr++ = 133;
+ *OpPtr++ = 134;
+ *OpPtr++ = 135;
+ *OpLen = (UINT8) ((OpPtr - OpLen) - 1);
+
+ *OpPtr++ = DHCP4_END;
+
+ This->Data->SetupCompleted = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+/* eof - PxeDhcp4Setup.c */
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c
new file mode 100644
index 0000000000..2e750522ca
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c
@@ -0,0 +1,1086 @@
+/** @file
+
+Copyright (c) 2004 - 2005, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+ support.c
+
+Abstract:
+ Miscellaneous support routines for PxeDhcp4 protocol.
+
+
+**/
+
+
+#include "PxeDhcp4.h"
+
+#define DebugPrint(x)
+//
+// #define DebugPrint(x) Aprint x
+//
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+UINT16
+htons (
+ UINTN n
+ )
+{
+ return (UINT16) ((n >> 8) | (n << 8));
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+UINT32
+htonl (
+ UINTN n
+ )
+{
+ return (UINT32) ((n >> 24) | ((n >> 8) & 0xFF00) | ((n & 0xFF00) << 8) | (n << 24));
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+VOID
+EFIAPI
+timeout_notify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ ASSERT (Context);
+
+ if (Context != NULL) {
+ ((PXE_DHCP4_PRIVATE_DATA *) Context)->TimeoutOccurred = TRUE;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+VOID
+EFIAPI
+periodic_notify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ ASSERT (Context);
+
+ if (Context != NULL) {
+ ((PXE_DHCP4_PRIVATE_DATA *) Context)->PeriodicOccurred = TRUE;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return EFI_SUCCESS := Option was found
+ @return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL
+ @return EFI_INVALID_PARAMETER := OpCode == DHCP4_PAD
+ @return EFI_INVALID_PARAMETER := OpCode == DHCP4_END && Skip != 0
+ @return EFI_INVALID_PARAMETER := DHCP magik number in Packet is not valid
+ @return EFI_NOT_FOUND := op-code was not found in packet
+ @return EFI_INVALID_PARAMETER := If present, DHCP_MAX_MESSAGE_SIZE option
+ @return does not have a valid value.
+
+**/
+EFI_STATUS
+find_opt (
+ IN DHCP4_PACKET *Packet,
+ IN UINT8 OpCode,
+ IN UINTN Skip,
+ OUT DHCP4_OP **OpPtr
+ )
+{
+ UINTN msg_size;
+ UINTN buf_len;
+ UINTN n;
+ UINT8 *buf;
+ UINT8 *end_ptr;
+ UINT8 overload;
+
+ //
+ // Verify parameters.
+ //
+ if (Packet == NULL || OpPtr == NULL || OpCode == DHCP4_PAD || (OpCode == DHCP4_END && Skip != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet->dhcp4.magik != htonl (DHCP4_MAGIK_NUMBER)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Initialize search variables.
+ //
+ *OpPtr = NULL;
+
+ msg_size = DHCP4_MAX_PACKET_SIZE - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);
+
+ overload = 0;
+ end_ptr = NULL;
+
+ buf = Packet->dhcp4.options;
+ buf_len = msg_size - (Packet->dhcp4.options - Packet->raw);
+
+ //
+ // Start searching for requested option.
+ //
+ for (n = 0;;) {
+ //
+ // If match is found, decrement skip count and return
+ // when desired match is found.
+ //
+ if (buf[n] == OpCode) {
+ *OpPtr = (DHCP4_OP *) &buf[n];
+
+ if (Skip-- == 0) {
+ return EFI_SUCCESS;
+ }
+ }
+ //
+ // Skip past current option. Check for option overload
+ // and message size options since these will affect the
+ // amount of data to be searched.
+ //
+ switch (buf[n]) {
+ case DHCP4_PAD:
+ //
+ // Remember the first pad byte of a group. This
+ // could be the end of a badly formed packet.
+ //
+ if (end_ptr == NULL) {
+ end_ptr = &buf[n];
+ }
+
+ ++n;
+ break;
+
+ case DHCP4_END:
+ //
+ // If we reach the end we are done.
+ //
+ end_ptr = NULL;
+ return EFI_NOT_FOUND;
+
+ case DHCP4_OPTION_OVERLOAD:
+ //
+ // Remember the option overload value since it
+ // could cause the search to continue into
+ // the fname and sname fields.
+ //
+ end_ptr = NULL;
+
+ if (buf[n + 1] == 1) {
+ overload = buf[n + 2];
+ }
+
+ n += 2 + buf[n + 1];
+ break;
+
+ case DHCP4_MAX_MESSAGE_SIZE:
+ //
+ // Remember the message size value since it could
+ // change the amount of option buffer to search.
+ //
+ end_ptr = NULL;
+
+ if (buf[n + 1] == 2 && buf == Packet->dhcp4.options) {
+ msg_size = ((buf[n + 2] << 8) | buf[n + 3]) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);
+
+ if (msg_size < 328) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ buf_len = msg_size - (Packet->dhcp4.options - Packet->raw);
+
+ if (n + 2 + buf[n + 1] > buf_len) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ /* fall thru */
+ default:
+ end_ptr = NULL;
+
+ n += 2 + buf[n + 1];
+ }
+ //
+ // Keep searching until the end of the buffer is reached.
+ //
+ if (n < buf_len) {
+ continue;
+ }
+ //
+ // Reached end of current buffer. Check if we are supposed
+ // to search the fname and sname buffers.
+ //
+ if (buf == Packet->dhcp4.options &&
+ (overload == DHCP4_OVERLOAD_FNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)
+ ) {
+ buf = Packet->dhcp4.fname;
+ buf_len = 128;
+ n = 0;
+ continue;
+ }
+
+ if (buf != Packet->dhcp4.sname && (overload == DHCP4_OVERLOAD_SNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)) {
+ buf = Packet->dhcp4.sname;
+ buf_len = 64;
+ n = 0;
+ continue;
+ }
+ //
+ // End of last buffer reached. If this was a search
+ // for the end of the options, go back to the start
+ // of the current pad block.
+ //
+ if (OpCode == DHCP4_END && end_ptr != NULL) {
+ *OpPtr = (DHCP4_OP *) end_ptr;
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL
+ @return EFI_INVALID_PARAMETER := OpPtr->op == DHCP4_PAD || OpPtr->op == DHCP4_END
+ @return EFI_INVALID_PARAMETER := DHCP magik number in DHCP packet is not valid
+ @return EFI_INVALID_PARAMETER := If DHCP_MAX_MESSAGE_SIZE option is present and
+ @return is not valid
+ @return EFI_INVALID_PARAMETER := If DHCP_OPTION_OVERLOAD option is present and
+ @return is not valid
+ @return EFI_DEVICE_ERROR := Cannot determine end of packet
+ @return EFI_BUFFER_TOO_SMALL := Not enough room in packet to add option
+ @return EFI_SUCCESS := Option added to DHCP packet
+
+**/
+EFI_STATUS
+add_opt (
+ IN DHCP4_PACKET *Packet,
+ IN DHCP4_OP *OpPtr
+ )
+{
+ EFI_STATUS efi_status;
+ DHCP4_OP *msg_size_op;
+ DHCP4_OP *overload_op;
+ DHCP4_OP *op;
+ UINTN msg_size;
+ UINTN buf_len;
+ UINT32 magik;
+ UINT8 *buf;
+
+ //
+ // Verify parameters.
+ //
+ ASSERT (Packet);
+ ASSERT (OpPtr);
+
+ if (Packet == NULL || OpPtr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (OpPtr->op) {
+ case DHCP4_PAD:
+ case DHCP4_END:
+ //
+ // No adding PAD or END.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Check the DHCP magik number.
+ //
+ CopyMem (&magik, &Packet->dhcp4.magik, 4);
+
+ if (magik != htonl (DHCP4_MAGIK_NUMBER)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Find the DHCP message size option.
+ //
+ msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE;
+
+ efi_status = find_opt (
+ Packet,
+ DHCP4_MAX_MESSAGE_SIZE,
+ 0,
+ &msg_size_op
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ if (efi_status != EFI_NOT_FOUND) {
+ DebugPrint (
+ ("%s:%d:%r\n",
+ __FILE__,
+ __LINE__,
+ efi_status)
+ );
+ return efi_status;
+ }
+
+ msg_size_op = NULL;
+ } else {
+ CopyMem (&msg_size, msg_size_op->data, 2);
+ msg_size = htons (msg_size);
+
+ if (msg_size < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // Find the DHCP option overload option.
+ //
+ efi_status = find_opt (
+ Packet,
+ DHCP4_OPTION_OVERLOAD,
+ 0,
+ &overload_op
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ if (efi_status != EFI_NOT_FOUND) {
+ DebugPrint (
+ ("%s:%d:%r\n",
+ __FILE__,
+ __LINE__,
+ efi_status)
+ );
+ return efi_status;
+ }
+
+ overload_op = NULL;
+ } else {
+ if (overload_op->len != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (overload_op->data[0]) {
+ case 1:
+ case 2:
+ case 3:
+ break;
+
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // Find the end of the packet.
+ //
+ efi_status = find_opt (Packet, DHCP4_END, 0, &op);
+
+ if (EFI_ERROR (efi_status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Find which buffer the end is in.
+ //
+ if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.options)) {
+ buf_len = (msg_size - ((UINT8 *) &Packet->dhcp4.options - (UINT8 *) &Packet->raw)) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);
+ } else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.fname)) {
+ buf_len = 128;
+ } else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.sname)) {
+ buf_len = 64;
+ } else {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Add option to current buffer if there is no overlow.
+ //
+ if ((UINTN) ((&op->op - buf) + 3 + op->len) < buf_len) {
+ CopyMem (op, OpPtr, OpPtr->len + 2);
+
+ op->data[op->len] = DHCP4_END;
+
+ return EFI_SUCCESS;
+ }
+ //
+ // Error if there is no space for option.
+ //
+ return EFI_BUFFER_TOO_SMALL;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return EFI_INVALID_PARAMETER := Private == NULL || Private->PxeBc == NULL
+ @return EFI_INVALID_PARAMETER := Only one of StationIp and SubnetMask is given
+ @return EFI_SUCCESS := UDP stack is ready
+ @return other := Error from PxeBc->SetIpFilter() or PxeBc->SetStationIp()
+
+**/
+EFI_STATUS
+start_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN OPTIONAL EFI_IP_ADDRESS *StationIp,
+ IN OPTIONAL EFI_IP_ADDRESS *SubnetMask
+ )
+{
+ EFI_PXE_BASE_CODE_IP_FILTER bcast_filter;
+ EFI_STATUS efi_status;
+
+ //
+ //
+ //
+ ASSERT (Private);
+ ASSERT (Private->PxeBc);
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Private->PxeBc == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (StationIp != NULL && SubnetMask == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (StationIp == NULL && SubnetMask != NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Setup broadcast receive filter...
+ //
+ ZeroMem (&bcast_filter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+
+ bcast_filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;
+ bcast_filter.IpCnt = 0;
+
+ efi_status = Private->PxeBc->SetIpFilter (
+ Private->PxeBc,
+ &bcast_filter
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ return efi_status;
+ }
+ //
+ // Configure station IP address and subnet mask...
+ //
+ efi_status = Private->PxeBc->SetStationIp (
+ Private->PxeBc,
+ StationIp,
+ SubnetMask
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ }
+
+ return efi_status;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+VOID
+stop_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private
+ )
+{
+ //
+ //
+ //
+ ASSERT (Private);
+ ASSERT (Private->PxeBc);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+
+**/
+EFI_STATUS
+start_receive_events (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN UINTN SecondsTimeout
+ )
+{
+ EFI_STATUS efi_status;
+ UINTN random;
+
+ //
+ //
+ //
+ ASSERT (Private);
+ ASSERT (SecondsTimeout);
+
+ if (Private == NULL || SecondsTimeout == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Need a bettern randomizer...
+ // For now adjust the timeout value by the least significant
+ // digit in the MAC address.
+ //
+ random = 0;
+
+ if (Private->PxeDhcp4.Data != NULL) {
+ if (Private->PxeDhcp4.Data->Discover.dhcp4.hlen != 0 && Private->PxeDhcp4.Data->Discover.dhcp4.hlen <= 16) {
+ random = 0xFFF & Private->PxeDhcp4.Data->Discover.dhcp4.chaddr[Private->PxeDhcp4.Data->Discover.dhcp4.hlen - 1];
+ }
+ }
+ //
+ // Setup timeout event and start timer.
+ //
+ efi_status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ &timeout_notify,
+ Private,
+ &Private->TimeoutEvent
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ return efi_status;
+ }
+
+ efi_status = gBS->SetTimer (
+ Private->TimeoutEvent,
+ TimerRelative,
+ SecondsTimeout * 10000000 + random
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ gBS->CloseEvent (Private->TimeoutEvent);
+ return efi_status;
+ }
+
+ Private->TimeoutOccurred = FALSE;
+
+ //
+ // Setup periodic event for callbacks
+ //
+ efi_status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ &periodic_notify,
+ Private,
+ &Private->PeriodicEvent
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ gBS->CloseEvent (Private->TimeoutEvent);
+ return efi_status;
+ }
+
+ efi_status = gBS->SetTimer (
+ Private->PeriodicEvent,
+ TimerPeriodic,
+ 1000000
+ ); /* 1/10th second */
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ gBS->CloseEvent (Private->TimeoutEvent);
+ gBS->CloseEvent (Private->PeriodicEvent);
+ return efi_status;
+ }
+
+ Private->PeriodicOccurred = FALSE;
+
+ return EFI_SUCCESS;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+VOID
+stop_receive_events (
+ IN PXE_DHCP4_PRIVATE_DATA *Private
+ )
+{
+ //
+ //
+ //
+ ASSERT (Private);
+
+ if (Private == NULL) {
+ return ;
+ }
+ //
+ //
+ //
+ gBS->CloseEvent (Private->TimeoutEvent);
+ Private->TimeoutOccurred = FALSE;
+
+ //
+ //
+ //
+ gBS->CloseEvent (Private->PeriodicEvent);
+ Private->PeriodicOccurred = FALSE;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return EFI_INVALID_PARAMETER := Private == NULL || dest_ip == NULL ||
+ @return buffer == NULL || BufferSize < 300 || Private->PxeBc == NULL
+ @return EFI_SUCCESS := Buffer was transmitted
+ @return other := Return from PxeBc->UdpWrite()
+
+**/
+EFI_STATUS
+tx_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN EFI_IP_ADDRESS *dest_ip,
+ IN OPTIONAL EFI_IP_ADDRESS *gateway_ip,
+ IN EFI_IP_ADDRESS *src_ip,
+ IN VOID *buffer,
+ IN UINTN BufferSize
+ )
+{
+ EFI_PXE_BASE_CODE_UDP_PORT dest_port;
+ EFI_PXE_BASE_CODE_UDP_PORT src_port;
+ EFI_IP_ADDRESS zero_ip;
+
+ //
+ //
+ //
+ ASSERT (Private);
+ ASSERT (dest_ip);
+ ASSERT (buffer);
+ ASSERT (BufferSize >= 300);
+
+ if (Private == NULL || dest_ip == NULL || buffer == NULL || BufferSize < 300) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ASSERT (Private->PxeBc);
+
+ if (Private->PxeBc == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Transmit DHCP discover packet...
+ //
+ ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));
+
+ if (src_ip == NULL) {
+ src_ip = &zero_ip;
+ }
+
+ dest_port = DHCP4_SERVER_PORT;
+ src_port = DHCP4_CLIENT_PORT;
+
+ return Private->PxeBc->UdpWrite (
+ Private->PxeBc,
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
+ dest_ip,
+ &dest_port,
+ gateway_ip,
+ src_ip,
+ &src_port,
+ NULL,
+ NULL,
+ &BufferSize,
+ buffer
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/**
+
+ @return EFI_INVALID_PARAMETER :=
+ @return EFI_SUCCESS := Packet received
+ @return other := Return from PxeBc->UdpRead()
+
+**/
+EFI_STATUS
+rx_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ OUT VOID *buffer,
+ IN OUT UINTN *BufferSize,
+ IN OUT EFI_IP_ADDRESS *dest_ip,
+ IN OUT EFI_IP_ADDRESS *src_ip,
+ IN UINT16 op_flags
+ )
+{
+ EFI_PXE_BASE_CODE_UDP_PORT dest_port;
+ EFI_PXE_BASE_CODE_UDP_PORT src_port;
+
+ //
+ //
+ //
+ ASSERT (Private);
+ ASSERT (buffer);
+ ASSERT (dest_ip);
+ ASSERT (src_ip);
+
+ if (Private == NULL || buffer == NULL || dest_ip == NULL || src_ip == NULL || BufferSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ASSERT (Private->PxeBc);
+
+ if (Private->PxeBc == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Check for packet
+ //
+ *BufferSize = sizeof (DHCP4_PACKET);
+
+ dest_port = DHCP4_CLIENT_PORT;
+ src_port = DHCP4_SERVER_PORT;
+
+ return Private->PxeBc->UdpRead (
+ Private->PxeBc,
+ op_flags,
+ dest_ip,
+ &dest_port,
+ src_ip,
+ &src_port,
+ NULL,
+ NULL,
+ BufferSize,
+ buffer
+ );
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+EFI_STATUS
+tx_rx_udp (
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN OUT EFI_IP_ADDRESS *ServerIp,
+ IN OPTIONAL EFI_IP_ADDRESS *gateway_ip,
+ IN OPTIONAL EFI_IP_ADDRESS *client_ip,
+ IN OPTIONAL EFI_IP_ADDRESS *SubnetMask,
+ IN DHCP4_PACKET *tx_pkt,
+ OUT DHCP4_PACKET *rx_pkt,
+ IN INTN (*rx_vfy)(
+ IN PXE_DHCP4_PRIVATE_DATA *Private,
+ IN DHCP4_PACKET *tx_pkt,
+ IN DHCP4_PACKET *rx_pkt,
+ IN UINTN rx_pkt_size
+ ),
+ IN UINTN SecondsTimeout
+ )
+/*++
+Routine description:
+ Transmit DHCP packet and wait for replies.
+
+Parameters:
+ Private := Pointer to PxeDhcp4 private data
+ ServerIp := Pointer to server IP address
+ gateway_ip := Pointer to gateway IP address or NULL
+ client_ip := Pointer to client IP address or NULL
+ SubnetMask := Pointer to subnet mask or NULL
+ tx_pkt := Pointer to DHCP packet to transmit
+ rx_pkt := Pointer to DHCP packet receive buffer
+ rx_vfy := Pointer to DHCP packet receive verification routine
+ SecondsTimeout := Number of seconds until timeout
+
+Returns:
+ EFI_INVALID_PARAMETER := Private == NULL || ServerIp == NULL ||
+ tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL || Private->PxeBc == NULL
+ EFI_ABORTED := Receive aborted
+ EFI_TIMEOUT := No packets received
+ EFI_SUCCESS := Packet(s) received
+ other := Returns from other PxeDhcp4 support routines
+--*/
+{
+ EFI_PXE_DHCP4_CALLBACK_STATUS CallbackStatus;
+ EFI_IP_ADDRESS dest_ip;
+ EFI_IP_ADDRESS src_ip;
+ EFI_STATUS efi_status;
+ DHCP4_OP *msg_size_op;
+ UINTN pkt_size;
+ UINTN n;
+ UINT16 msg_size;
+ UINT16 op_flags;
+ BOOLEAN done_flag;
+ BOOLEAN got_packet;
+
+ //
+ // Bad programmer check...
+ //
+ ASSERT (Private);
+ ASSERT (ServerIp);
+ ASSERT (tx_pkt);
+ ASSERT (rx_pkt);
+ ASSERT (rx_vfy);
+
+ if (Private == NULL || ServerIp == NULL || tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ASSERT (Private->PxeBc);
+
+ if (Private->PxeBc == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Enable UDP...
+ //
+ efi_status = start_udp (Private, client_ip, SubnetMask);
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ return efi_status;
+ }
+ //
+ // Get length of transmit packet...
+ //
+ msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE;
+
+ efi_status = find_opt (
+ tx_pkt,
+ DHCP4_MAX_MESSAGE_SIZE,
+ 0,
+ &msg_size_op
+ );
+
+ if (!EFI_ERROR (efi_status)) {
+ CopyMem (&msg_size, msg_size_op->data, 2);
+
+ if ((msg_size = htons (msg_size)) < 328) {
+ msg_size = 328;
+ }
+ }
+ //
+ // Transmit packet...
+ //
+ efi_status = tx_udp (
+ Private,
+ ServerIp,
+ gateway_ip,
+ client_ip,
+ tx_pkt,
+ msg_size - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE)
+ );
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ stop_udp (Private);
+ return efi_status;
+ }
+ //
+ // Enable periodic and timeout events...
+ //
+ efi_status = start_receive_events (Private, SecondsTimeout);
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ stop_udp (Private);
+ return efi_status;
+ }
+ //
+ // Wait for packet(s)...
+ //
+#if 0
+ if (!client_ip) {
+ Aprint ("client_ip == NULL ");
+ } else {
+ Aprint (
+ "client_ip == %d.%d.%d.%d ",
+ client_ip->v4.Addr[0],
+ client_ip->v4.Addr[1],
+ client_ip->v4.Addr[2],
+ client_ip->v4.Addr[3]
+ );
+ }
+
+ if (!ServerIp) {
+ Aprint ("ServerIp == NULL\n");
+ } else {
+ Aprint (
+ "ServerIp == %d.%d.%d.%d\n",
+ ServerIp->v4.Addr[0],
+ ServerIp->v4.Addr[1],
+ ServerIp->v4.Addr[2],
+ ServerIp->v4.Addr[3]
+ );
+ }
+#endif
+
+ done_flag = FALSE;
+ got_packet = FALSE;
+
+ while (!done_flag) {
+ //
+ // Check for timeout event...
+ //
+ if (Private->TimeoutOccurred) {
+ efi_status = EFI_SUCCESS;
+ break;
+ }
+ //
+ // Check for periodic event...
+ //
+ if (Private->PeriodicOccurred && Private->callback != NULL) {
+ CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE;
+
+ if (Private->callback->Callback != NULL) {
+ CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, 0, NULL);
+ }
+
+ switch (CallbackStatus) {
+ case EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE:
+ break;
+
+ case EFI_PXE_DHCP4_CALLBACK_STATUS_ABORT:
+ default:
+ stop_receive_events (Private);
+ stop_udp (Private);
+ return EFI_ABORTED;
+ }
+
+ Private->PeriodicOccurred = FALSE;
+ }
+ //
+ // Check for packet...
+ //
+ if (client_ip == NULL) {
+ SetMem (&dest_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
+ } else {
+ CopyMem (&dest_ip, client_ip, sizeof (EFI_IP_ADDRESS));
+ }
+
+ SetMem (&src_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
+
+ if (CompareMem (&src_ip, &ServerIp, sizeof (EFI_IP_ADDRESS))) {
+ ZeroMem (&src_ip, sizeof (EFI_IP_ADDRESS));
+ op_flags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP;
+ } else {
+ op_flags = 0;
+ }
+
+ efi_status = rx_udp (
+ Private,
+ rx_pkt,
+ &pkt_size,
+ &dest_ip,
+ &src_ip,
+ op_flags
+ );
+
+ if (efi_status == EFI_TIMEOUT) {
+ efi_status = EFI_SUCCESS;
+ continue;
+ }
+
+ if (EFI_ERROR (efi_status)) {
+ break;
+ }
+ //
+ // Some basic packet sanity checks..
+ //
+ if (pkt_size < 300) {
+ continue;
+ }
+
+ if (rx_pkt->dhcp4.op != BOOTP_REPLY) {
+ continue;
+ }
+
+ if (tx_pkt->dhcp4.htype != rx_pkt->dhcp4.htype) {
+ continue;
+ }
+
+ if ((n = tx_pkt->dhcp4.hlen) != rx_pkt->dhcp4.hlen) {
+ continue;
+ }
+
+ if (CompareMem (&tx_pkt->dhcp4.xid, &rx_pkt->dhcp4.xid, 4)) {
+ continue;
+ }
+
+ if (n != 0) {
+ if (n >= 16) {
+ n = 16;
+ }
+
+ if (CompareMem (tx_pkt->dhcp4.chaddr, rx_pkt->dhcp4.chaddr, n)) {
+ continue;
+ }
+ }
+ //
+ // Internal callback packet verification...
+ //
+ switch ((*rx_vfy) (Private, tx_pkt, rx_pkt, pkt_size)) {
+ case -2: /* ignore and stop */
+ stop_receive_events (Private);
+ stop_udp (Private);
+ return EFI_ABORTED;
+
+ case -1: /* ignore and wait */
+ continue;
+
+ case 0: /* accept and wait */
+ break;
+
+ case 1: /* accept and stop */
+ done_flag = TRUE;
+ break;
+
+ default:
+ ASSERT (0);
+ }
+ //
+ // External callback packet verification...
+ //
+ CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE;
+
+ if (Private->callback != NULL) {
+ if (Private->callback->Callback != NULL) {
+ CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, (UINT32) pkt_size, rx_pkt);
+ }
+ }
+
+ switch (CallbackStatus) {
+ case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_CONTINUE:
+ continue;
+
+ case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_ABORT:
+ done_flag = TRUE;
+ break;
+
+ case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_ABORT:
+ stop_receive_events (Private);
+ stop_udp (Private);
+ return EFI_ABORTED;
+
+ case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE:
+ default:
+ break;
+ }
+ //
+ // We did! We did get a packet!
+ //
+ got_packet = TRUE;
+ }
+ //
+ //
+ //
+ stop_receive_events (Private);
+ stop_udp (Private);
+
+ if (EFI_ERROR (efi_status)) {
+ DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
+ return efi_status;
+ }
+
+ if (got_packet) {
+ return EFI_SUCCESS;
+ } else {
+ return EFI_TIMEOUT;
+ }
+}
+
+/* eof - support.c */
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/snp.h b/MdeModulePkg/Universal/Network/SnpDxe/snp.h
index 624a44c971..90c9f3d563 100644
--- a/MdeModulePkg/Universal/Network/SnpDxe/snp.h
+++ b/MdeModulePkg/Universal/Network/SnpDxe/snp.h
@@ -41,20 +41,6 @@ Revision history:
#define FOUR_GIGABYTES (UINT64) 0x100000000ULL
-//
-// Driver Consumed Protocol Prototypes
-//
-//@MT:#include EFI_PROTOCOL_DEFINITION (DevicePath)
-//@MT:#include EFI_PROTOCOL_DEFINITION (PciIo)
-//@MT:#include EFI_PROTOCOL_DEFINITION (EfiNetworkInterfaceIdentifier)
-
-//
-// Driver Produced Protocol Prototypes
-//
-//@MT:#include EFI_PROTOCOL_DEFINITION (DriverBinding)
-//@MT:#include EFI_PROTOCOL_DEFINITION (ComponentName)
-//@MT:#include EFI_PROTOCOL_DEFINITION (ComponentName2)
-//@MT:#include EFI_PROTOCOL_DEFINITION (SimpleNetwork)
#define SNP_DRIVER_SIGNATURE EFI_SIGNATURE_32 ('s', 'n', 'd', 's')
#define MAX_MAP_LENGTH 100
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/start.c b/MdeModulePkg/Universal/Network/SnpDxe/start.c
index 0a43f8e60c..10c9554a0f 100644
--- a/MdeModulePkg/Universal/Network/SnpDxe/start.c
+++ b/MdeModulePkg/Universal/Network/SnpDxe/start.c
@@ -33,7 +33,7 @@ pxe_start (
SNP_DRIVER *snp
)
{
- PXE_CPB_START_30 *cpb;
+ PXE_CPB_START_30 *cpb;
PXE_CPB_START_31 *cpb_31;
cpb = snp->cpb;
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
index ea7d732b84..536a01a1d5 100644
--- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
@@ -118,11 +118,11 @@ Tcp4GetMode (
AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr;
- EFI_IP4 (AccessPoint->StationAddress) = Tcb->LocalEnd.Ip;
+ NetCopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
AccessPoint->SubnetMask = Tcb->SubnetMask;
AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
- EFI_IP4 (AccessPoint->RemoteAddress) = Tcb->RemoteEnd.Ip;
+ NetCopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
@@ -428,11 +428,11 @@ Tcp4ConfigurePcb (
Tcb->TTL = CfgData->TimeToLive;
Tcb->TOS = CfgData->TypeOfService;
- Tcb->LocalEnd.Ip = EFI_IP4 (CfgData->AccessPoint.StationAddress);
+ NetCopyMem (&Tcb->LocalEnd.Ip, &CfgData->AccessPoint.StationAddress, sizeof (IP4_ADDR));
Tcb->LocalEnd.Port = HTONS (CfgData->AccessPoint.StationPort);
Tcb->SubnetMask = CfgData->AccessPoint.SubnetMask;
- Tcb->RemoteEnd.Ip = EFI_IP4 (CfgData->AccessPoint.RemoteAddress);
+ NetCopyMem (&Tcb->RemoteEnd.Ip, &CfgData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));
Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort);
Option = CfgData->ControlOption;
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
index 002cd61089..d7f9fe73f5 100644
--- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
@@ -143,7 +143,6 @@ Tcp4DestroyTimer (
mTcp4Timer.TimerEvent = NULL;
}
-//@MT: EFI_DRIVER_ENTRY_POINT (Tcp4DriverEntryPoint)
EFI_STATUS
EFIAPI
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c
index 2f7a49fcb8..898d18f611 100644
--- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c
@@ -103,8 +103,8 @@ TcpSendIpPacket (
Override.TimeToLive = 255;
Override.DoNotFragment = FALSE;
Override.Protocol = EFI_IP_PROTO_TCP;
- EFI_IP4 (Override.GatewayAddress) = 0;
- EFI_IP4 (Override.SourceAddress) = Src;
+ NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+ NetCopyMem (&Override.SourceAddress, &Src, sizeof (EFI_IPv4_ADDRESS));
Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
index 8f5a997957..4d2e80fcad 100644
--- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
@@ -144,6 +144,7 @@ Tcp4Configure (
EFI_TCP4_OPTION *Option;
SOCKET *Sock;
EFI_STATUS Status;
+ IP4_ADDR Ip;
if (NULL == This) {
return EFI_INVALID_PARAMETER;
@@ -153,26 +154,25 @@ Tcp4Configure (
// Tcp protocol related parameter check will be conducted here
//
if (NULL != TcpConfigData) {
- if ((EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) != 0) &&
- !Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.RemoteAddress), 0)) {
+
+ NetCopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));
+ if ((Ip != 0) && !Ip4IsUnicast (NTOHL (Ip), 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TcpConfigData->AccessPoint.ActiveFlag &&
+ (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) {
return EFI_INVALID_PARAMETER;
}
if (!TcpConfigData->AccessPoint.UseDefaultAddress) {
- if (!Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.StationAddress), 0) ||
- !IP4_IS_VALID_NETMASK (EFI_NTOHL (TcpConfigData->AccessPoint.SubnetMask))
- ) {
+
+ NetCopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR));
+ if (!Ip4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (Ip))) {
return EFI_INVALID_PARAMETER;
}
}
- if (TcpConfigData->AccessPoint.ActiveFlag &&
- (0 == TcpConfigData->AccessPoint.RemotePort ||
- (EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) == 0))
- ) {
- return EFI_INVALID_PARAMETER;
- }
-
Option = TcpConfigData->ControlOption;
if ((NULL != Option) &&
(Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h
index dba46aa80a..3e6575be92 100644
--- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h
@@ -32,7 +32,6 @@ extern UINT16 mTcp4RandomPort;
//
// Driver Produced Protocol Prototypes
//
-//@MT:#include EFI_PROTOCOL_PRODUCER (Tcp4)
#define TCP4_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Tcp", PrintArg)
#define TCP4_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Tcp", PrintArg)
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
index 9cb55a567d..1cc8488111 100644
--- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
@@ -267,7 +267,7 @@ TcpFindTcbByPeer (
NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
- if ((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip) &&
+ if (EFI_IP4_EQUAL (*Addr, Tcb->LocalEnd.Ip) &&
(LocalPort == Tcb->LocalEnd.Port)) {
return TRUE;
@@ -277,7 +277,7 @@ TcpFindTcbByPeer (
NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
- if (((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip)) &&
+ if (EFI_IP4_EQUAL (*Addr, Tcb->LocalEnd.Ip) &&
(LocalPort == Tcb->LocalEnd.Port)) {
return TRUE;
@@ -983,9 +983,9 @@ TcpSetVariableData (
// This tcp instance belongs to the Tcp4Service.
//
Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;
- EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip;
+ NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);
- EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip;
+ NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);
Tcp4ServicePoint++;
@@ -1005,9 +1005,9 @@ TcpSetVariableData (
// This tcp instance belongs to the Tcp4Service.
//
Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;
- EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip;
+ NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);
- EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip;
+ NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);
Tcp4ServicePoint++;
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
index 7b47ef44a9..ada73d79db 100644
--- a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
@@ -473,7 +473,6 @@ Udp4ServiceBindingDestroyChild (
return EFI_SUCCESS;
}
-//@MT: EFI_DRIVER_ENTRY_POINT (Udp4DriverEntryPoint)
EFI_STATUS
EFIAPI
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
index e6fddd8052..e5576820a6 100644
--- a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
@@ -424,7 +424,7 @@ Udp4FindInstanceByPort (
continue;
}
- if (EFI_IP_EQUAL (ConfigData->StationAddress, *Address) &&
+ if (EFI_IP4_EQUAL (ConfigData->StationAddress, *Address) &&
(ConfigData->StationPort == Port)) {
//
// if both the address and the port are the same, return TRUE.
@@ -566,8 +566,8 @@ Udp4IsReconfigurable (
}
if (!NewConfigData->UseDefaultAddress &&
- (!EFI_IP_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) ||
- !EFI_IP_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) {
+ (!EFI_IP4_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) ||
+ !EFI_IP4_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) {
//
// If the instance doesn't use the default address, and the new address or
// new subnet mask is different from the old values.
@@ -576,15 +576,14 @@ Udp4IsReconfigurable (
}
}
- if (!EFI_IP_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) {
+ if (!EFI_IP4_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) {
//
// The remoteaddress is not the same.
//
return FALSE;
}
- if ((EFI_IP4 (NewConfigData->RemoteAddress) != 0) &&
- (NewConfigData->RemotePort != OldConfigData->RemotePort)) {
+ if (!EFI_IP4_EQUAL (NewConfigData->RemoteAddress, mZeroIp4Addr) && (NewConfigData->RemotePort != OldConfigData->RemotePort)) {
//
// The RemotePort differs if it's designated in the configdata.
//
@@ -667,6 +666,7 @@ Udp4ValidateTxToken (
EFI_UDP4_CONFIG_DATA *ConfigData;
EFI_UDP4_SESSION_DATA *UdpSessionData;
IP4_ADDR SourceAddress;
+ IP4_ADDR GatewayAddress;
if (TxToken->Event == NULL) {
return EFI_INVALID_PARAMETER;
@@ -700,12 +700,15 @@ Udp4ValidateTxToken (
return EFI_INVALID_PARAMETER;
}
- if ((TxData->GatewayAddress != NULL) &&
- !Ip4IsUnicast(EFI_NTOHL (*(TxData->GatewayAddress)), 0)) {
- //
- // The specified GatewayAddress is not a unicast IPv4 address while it's not 0.
- //
- return EFI_INVALID_PARAMETER;
+ if (TxData->GatewayAddress != NULL) {
+ NetCopyMem (&GatewayAddress, TxData->GatewayAddress, sizeof (IP4_ADDR));
+
+ if (!Ip4IsUnicast (NTOHL (GatewayAddress), 0)) {
+ //
+ // The specified GatewayAddress is not a unicast IPv4 address while it's not 0.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
}
ConfigData = &Instance->ConfigData;
@@ -713,9 +716,9 @@ Udp4ValidateTxToken (
if (UdpSessionData != NULL) {
- SourceAddress = EFI_NTOHL (UdpSessionData->SourceAddress);
+ NetCopyMem (&SourceAddress, &UdpSessionData->SourceAddress, sizeof (IP4_ADDR));
- if ((SourceAddress != 0) && !Ip4IsUnicast (SourceAddress, 0)) {
+ if ((SourceAddress != 0) && !Ip4IsUnicast (HTONL (SourceAddress), 0)) {
//
// Check whether SourceAddress is a valid IPv4 address in case it's not zero.
// The configured station address is used if SourceAddress is zero.
@@ -730,13 +733,13 @@ Udp4ValidateTxToken (
return EFI_INVALID_PARAMETER;
}
- if (EFI_IP4 (UdpSessionData->DestinationAddress) == 0) {
+ if (EFI_IP4_EQUAL (UdpSessionData->DestinationAddress, mZeroIp4Addr)) {
//
// The DestinationAddress specified in the UdpSessionData is 0.
//
return EFI_INVALID_PARAMETER;
}
- } else if (EFI_IP4 (ConfigData->RemoteAddress) == 0) {
+ } else if (EFI_IP4_EQUAL (ConfigData->RemoteAddress, mZeroIp4Addr)) {
//
// the configured RemoteAddress is all zero, and the user doens't override the
// destination address.
@@ -959,9 +962,9 @@ Udp4LeaveGroup (
McastIp = Arg;
- if ((McastIp != NULL) && ((UINTN) EFI_IP4 (*McastIp) != (UINTN) (Item->Key))) {
+ if ((McastIp != NULL) && (!EFI_IP4_EQUAL (*McastIp, (UINTN) Item->Key))) {
//
- // McastIp is not NULL and the multicast address contained in the Item
+ // McastIp is not NULL and the multicast address contained in the Item
// is not the same as McastIp.
//
return EFI_SUCCESS;
@@ -1169,16 +1172,16 @@ Udp4MatchDgram (
return FALSE;
}
- if ((EFI_IP4 (ConfigData->RemoteAddress) != 0) &&
- !EFI_IP_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) {
+ if (!EFI_IP4_EQUAL (ConfigData->RemoteAddress, mZeroIp4Addr) &&
+ !EFI_IP4_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) {
//
// This datagram doesn't come from the instance's specified sender.
//
return FALSE;
}
- if ((EFI_IP4 (ConfigData->StationAddress) == 0) ||
- EFI_IP_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) {
+ if (EFI_IP4_EQUAL (ConfigData->StationAddress, mZeroIp4Addr) ||
+ EFI_IP4_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) {
//
// The instance is configured to receive datagrams destinated to any station IP or
// the destination address of this datagram matches the configured station IP.
@@ -1186,7 +1189,7 @@ Udp4MatchDgram (
return TRUE;
}
- Destination = EFI_IP4 (Udp4Session->DestinationAddress);
+ NetCopyMem (&Destination, &Udp4Session->DestinationAddress, sizeof (IP4_ADDR));
if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) {
//
@@ -1513,11 +1516,12 @@ Udp4Demultiplex (
gRT->GetTime (&RxData.TimeStamp, NULL);
- Udp4Session = &RxData.UdpSession;
- EFI_IP4 (Udp4Session->SourceAddress) = NetSession->Source;
- EFI_IP4 (Udp4Session->DestinationAddress) = NetSession->Dest;
- Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort);
- Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort);
+ Udp4Session = &RxData.UdpSession;
+ Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort);
+ Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort);
+
+ NetCopyMem (&Udp4Session->SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS));
+ NetCopyMem (&Udp4Session->DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));
//
// Trim the UDP header.
@@ -1637,12 +1641,13 @@ Udp4SendPortUnreach (
//
// Fill the override data.
//
- Override.DoNotFragment = FALSE;
- Override.TypeOfService = 0;
- Override.TimeToLive = 255;
- Override.Protocol = EFI_IP_PROTO_ICMP;
- EFI_IP4 (Override.SourceAddress) = NetSession->Dest;
- EFI_IP4 (Override.GatewayAddress) = 0;
+ Override.DoNotFragment = FALSE;
+ Override.TypeOfService = 0;
+ Override.TimeToLive = 255;
+ Override.Protocol = EFI_IP_PROTO_ICMP;
+
+ NetCopyMem (&Override.SourceAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));
+ NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
//
// Send out this icmp packet.
@@ -1682,10 +1687,11 @@ Udp4IcmpHandler (
Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL);
- EFI_IP4 (Udp4Session.SourceAddress) = NetSession->Source;
- EFI_IP4 (Udp4Session.DestinationAddress) = NetSession->Dest;
- Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort);
- Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort);
+ NetCopyMem (&Udp4Session.SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS));
+ NetCopyMem (&Udp4Session.DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));
+
+ Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort);
+ Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort);
NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
//
@@ -1696,7 +1702,7 @@ Udp4IcmpHandler (
if (!Instance->Configured ||
Instance->ConfigData.AcceptPromiscuous ||
Instance->ConfigData.AcceptAnyPort ||
- (EFI_IP4 (Instance->ConfigData.StationAddress) == 0)) {
+ EFI_IP4_EQUAL (Instance->ConfigData.StationAddress, mZeroIp4Addr)) {
//
// Don't try to deliver the ICMP error to this instance if it is not configured,
// or it's configured to be promiscuous or accept any port or accept all the
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c
index 5343bd5c4b..b3daacba23 100644
--- a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c
@@ -153,6 +153,8 @@ Udp4Configure (
IP4_ADDR SubnetMask;
IP4_ADDR RemoteAddress;
EFI_IP4_CONFIG_DATA Ip4ConfigData;
+ IP4_ADDR LocalAddr;
+ IP4_ADDR RemoteAddr;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
@@ -171,9 +173,14 @@ Udp4Configure (
if (UdpConfigData != NULL) {
- StationAddress = EFI_NTOHL (UdpConfigData->StationAddress);
- SubnetMask = EFI_NTOHL (UdpConfigData->SubnetMask);
- RemoteAddress = EFI_NTOHL (UdpConfigData->RemoteAddress);
+ NetCopyMem (&StationAddress, &UdpConfigData->StationAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&SubnetMask, &UdpConfigData->SubnetMask, sizeof (IP4_ADDR));
+ NetCopyMem (&RemoteAddress, &UdpConfigData->RemoteAddress, sizeof (IP4_ADDR));
+
+ StationAddress = NTOHL (StationAddress);
+ SubnetMask = NTOHL (SubnetMask);
+ RemoteAddress = NTOHL (RemoteAddress);
+
if (!UdpConfigData->UseDefaultAddress &&
(!IP4_IS_VALID_NETMASK (SubnetMask) ||
@@ -251,9 +258,11 @@ Udp4Configure (
//
// Pre calculate the checksum for the pseudo head, ignore the UDP length first.
//
+ NetCopyMem (&LocalAddr, &Instance->ConfigData.StationAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&RemoteAddr, &Instance->ConfigData.RemoteAddress, sizeof (IP4_ADDR));
Instance->HeadSum = NetPseudoHeadChecksum (
- EFI_IP4 (Instance->ConfigData.StationAddress),
- EFI_IP4 (Instance->ConfigData.RemoteAddress),
+ LocalAddr,
+ RemoteAddr,
EFI_IP_PROTO_UDP,
0
);
@@ -332,13 +341,21 @@ Udp4Groups (
UDP4_INSTANCE_DATA *Instance;
EFI_IP4_PROTOCOL *Ip;
EFI_TPL OldTpl;
+ IP4_ADDR McastIp;
- if ((This == NULL) ||
- (JoinFlag && (MulticastAddress == NULL)) ||
- (JoinFlag && !IP4_IS_MULTICAST (EFI_NTOHL (*MulticastAddress)))) {
+ if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) {
return EFI_INVALID_PARAMETER;
}
+ McastIp = 0;
+ if (JoinFlag) {
+ NetCopyMem (&McastIp, MulticastAddress, sizeof (IP4_ADDR));
+
+ if (IP4_IS_MULTICAST (NTOHL (McastIp))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
if (Instance->IsNoMapping) {
@@ -370,11 +387,7 @@ Udp4Groups (
//
if (JoinFlag) {
- NetMapInsertTail (
- &Instance->McastIps,
- (VOID *) (UINTN) EFI_IP4 (*MulticastAddress),
- NULL
- );
+ NetMapInsertTail (&Instance->McastIps, (VOID *) (UINTN) McastIp, NULL);
} else {
NetMapIterate (&Instance->McastIps, Udp4LeaveGroup, MulticastAddress);
@@ -506,6 +519,7 @@ Udp4Transmit (
NET_BUF *Packet;
EFI_UDP4_HEADER *Udp4Header;
EFI_UDP4_CONFIG_DATA *ConfigData;
+ IP4_ADDR Source;
IP4_ADDR Destination;
EFI_UDP4_TRANSMIT_DATA *TxData;
EFI_UDP4_SESSION_DATA *UdpSessionData;
@@ -590,25 +604,26 @@ Udp4Transmit (
// Set the SourceAddress, SrcPort and Destination according to the specified
// UdpSessionData.
//
- if (EFI_IP4 (UdpSessionData->SourceAddress) != 0) {
- Override.SourceAddress = UdpSessionData->SourceAddress;
+ if (!EFI_IP4_EQUAL (UdpSessionData->SourceAddress, mZeroIp4Addr)) {
+ NetCopyMem (&Override.SourceAddress, &UdpSessionData->SourceAddress, sizeof (EFI_IPv4_ADDRESS));
}
if (UdpSessionData->SourcePort != 0) {
Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort);
}
- Destination = EFI_IP4 (UdpSessionData->DestinationAddress);
-
if (UdpSessionData->DestinationPort != 0) {
Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort);
}
+ NetCopyMem (&Source, &Override.SourceAddress, sizeof (IP4_ADDR));
+ NetCopyMem (&Destination, &UdpSessionData->DestinationAddress, sizeof (IP4_ADDR));
+
//
// calculate the pseudo head checksum using the overridden parameters.
//
HeadSum = NetPseudoHeadChecksum (
- EFI_IP4 (Override.SourceAddress),
+ Source,
Destination,
EFI_IP_PROTO_UDP,
0
@@ -617,8 +632,9 @@ Udp4Transmit (
//
// UdpSessionData is NULL, use the address and port information previously configured.
//
- Destination = EFI_IP4 (ConfigData->RemoteAddress);
- HeadSum = Instance->HeadSum;
+ NetCopyMem (&Destination, &ConfigData->RemoteAddress, sizeof (IP4_ADDR));
+
+ HeadSum = Instance->HeadSum;
}
//
@@ -635,8 +651,12 @@ Udp4Transmit (
//
// Fill the IpIo Override data.
//
- EFI_IP4 (Override.GatewayAddress) = (TxData->GatewayAddress != NULL) ?
- EFI_IP4 (*(TxData->GatewayAddress)) : 0;
+ if (TxData->GatewayAddress != NULL) {
+ NetCopyMem (&Override.GatewayAddress, TxData->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+ } else {
+ NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+ }
+
Override.Protocol = EFI_IP_PROTO_UDP;
Override.TypeOfService = ConfigData->TypeOfService;
Override.TimeToLive = ConfigData->TimeToLive;