]> git.proxmox.com Git - mirror_edk2.git/commitdiff
Import ArpDxe, Dhcp4Dxe, Ip4Dxe, Mtftp4Dxe, PxeBcDxe and PxeDhcp4Dxe.
authorvanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 30 Jul 2007 02:37:10 +0000 (02:37 +0000)
committervanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 30 Jul 2007 02:37:10 +0000 (02:37 +0000)
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3492 6f19259b-4bc3-4df7-8a09-765794883524

113 files changed:
MdeModulePkg/Include/Library/IpIoLib.h
MdeModulePkg/Include/Library/NetLib.h
MdeModulePkg/Include/Protocol/PxeDhcp4.h [new file with mode: 0644]
MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h [new file with mode: 0644]
MdeModulePkg/Include/Protocol/Tcp.h [new file with mode: 0644]
MdeModulePkg/Library/DxeNetLib/DxeNetLib.c
MdeModulePkg/MdeModulePkg.dec
MdeModulePkg/MdeModulePkg.dsc
MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf [new file with mode: 0644]
MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa [new file with mode: 0644]
MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c
MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c
MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c
MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c
MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h
MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c
MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Hton.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c [new file with mode: 0644]
MdeModulePkg/Universal/Network/SnpDxe/snp.h
MdeModulePkg/Universal/Network/SnpDxe/start.c
MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c
MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h
MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c

index 894e07bed4f08cabff6480931b45a24d49935ee7..e2c9f67559ef81631efbd9b7fc510bbd9c648da0 100644 (file)
@@ -73,11 +73,6 @@ typedef struct _ICMP_ERROR_INFO {
   BOOLEAN     Notify;\r
 } ICMP_ERROR_INFO;\r
 
   BOOLEAN     Notify;\r
 } ICMP_ERROR_INFO;\r
 
-//
-// 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)
 
 
 #define EFI_IP4_HEADER_LEN(HdrPtr) ((HdrPtr)->HeaderLength << 2)
 
index 150b97936ea26a0337a1b5910b4175ecf5d28e2d..fe240442ae812410e1c38948e329ad11f1acb8c4 100644 (file)
@@ -29,189 +29,189 @@ Abstract:
 #include <Protocol/DriverConfiguration.h>
 #include <Protocol/DriverDiagnostics.h>
 
 #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\r
+\r
+typedef UINT32          IP4_ADDR;\r
+typedef UINT32          TCP_SEQNO;\r
+typedef UINT16          TCP_PORTNO;\r
+\r
+enum {\r
+  NET_ETHER_ADDR_LEN    = 6,\r
+  NET_IFTYPE_ETHERNET   = 0x01,\r
+\r
+  EFI_IP_PROTO_UDP      = 0x11,\r
+  EFI_IP_PROTO_TCP      = 0x06,\r
+  EFI_IP_PROTO_ICMP     = 0x01,\r
+\r
+  //\r
+  // The address classfication\r
+  //\r
+  IP4_ADDR_CLASSA       = 1,\r
+  IP4_ADDR_CLASSB,\r
+  IP4_ADDR_CLASSC,\r
+  IP4_ADDR_CLASSD,\r
+  IP4_ADDR_CLASSE,\r
+\r
+  IP4_MASK_NUM          = 33,\r
+};\r
+\r
+#pragma pack(1)\r
+\r
+//\r
+// Ethernet head definition\r
+//\r
+typedef struct {\r
+  UINT8                 DstMac [NET_ETHER_ADDR_LEN];\r
+  UINT8                 SrcMac [NET_ETHER_ADDR_LEN];\r
+  UINT16                EtherType;\r
+} ETHER_HEAD;\r
+\r
+\r
+//\r
+// The EFI_IP4_HEADER is hard to use because the source and\r
+// destination address are defined as EFI_IPv4_ADDRESS, which\r
+// is a structure. Two structures can't be compared or masked\r
+// directly. This is why there is an internal representation.\r
+//\r
+typedef struct {\r
+#ifdef EFI_NET_LITTLE_ENDIAN\r
+  UINT8                 HeadLen : 4;\r
+  UINT8                 Ver     : 4;\r
+#else\r
+  UINT8                 Ver     : 4;\r
+  UINT8                 HeadLen : 4;\r
+#endif\r
+  UINT8                 Tos;\r
+  UINT16                TotalLen;\r
+  UINT16                Id;\r
+  UINT16                Fragment;\r
+  UINT8                 Ttl;\r
+  UINT8                 Protocol;\r
+  UINT16                Checksum;\r
+  IP4_ADDR              Src;\r
+  IP4_ADDR              Dst;\r
+} IP4_HEAD;\r
+\r
+\r
+//\r
+// ICMP head definition. ICMP message is categoried as either an error\r
+// message or query message. Two message types have their own head format.\r
+//\r
+typedef struct {\r
+  UINT8                 Type;\r
+  UINT8                 Code;\r
+  UINT16                Checksum;\r
+} IP4_ICMP_HEAD;\r
+\r
+typedef struct {\r
+  IP4_ICMP_HEAD         Head;\r
+  UINT32                Fourth; // 4th filed of the head, it depends on Type.\r
+  IP4_HEAD              IpHead;\r
+} IP4_ICMP_ERROR_HEAD;\r
+\r
+typedef struct {\r
+  IP4_ICMP_HEAD         Head;\r
+  UINT16                Id;\r
+  UINT16                Seq;\r
+} IP4_ICMP_QUERY_HEAD;\r
+\r
+\r
+//\r
+// UDP header definition\r
+//\r
+typedef struct {\r
+  UINT16                SrcPort;\r
+  UINT16                DstPort;\r
+  UINT16                Length;\r
+  UINT16                Checksum;\r
+} EFI_UDP4_HEADER;\r
+\r
+\r
+//\r
+// TCP header definition\r
+//\r
+typedef struct {\r
+  TCP_PORTNO            SrcPort;\r
+  TCP_PORTNO            DstPort;\r
+  TCP_SEQNO             Seq;\r
+  TCP_SEQNO             Ack;\r
+#ifdef EFI_NET_LITTLE_ENDIAN\r
+  UINT8                 Res     : 4;\r
+  UINT8                 HeadLen : 4;\r
+#else\r
+  UINT8                 HeadLen : 4;\r
+  UINT8                 Res     : 4;\r
+#endif\r
+  UINT8                 Flag;\r
+  UINT16                Wnd;\r
+  UINT16                Checksum;\r
+  UINT16                Urg;\r
+} TCP_HEAD;\r
+\r
+#pragma pack()\r
+\r
+#define NET_MAC_EQUAL(pMac1, pMac2, Len)     \\r
+    (NetCompareMem ((pMac1), (pMac2), Len) == 0)\r
+\r
+#define NET_MAC_IS_MULTICAST(Mac, BMac, Len) \\r
+    (((*((UINT8 *) Mac) & 0x01) == 0x01) && (!NET_MAC_EQUAL (Mac, BMac, Len)))\r
+\r
+#ifdef EFI_NET_LITTLE_ENDIAN\r
+#define NTOHL(x) (UINT32)((((UINT32) (x) & 0xff)     << 24) | \\r
+                          (((UINT32) (x) & 0xff00)   << 8)  | \\r
+                          (((UINT32) (x) & 0xff0000) >> 8)  | \\r
+                          (((UINT32) (x) & 0xff000000) >> 24))\r
+\r
+#define HTONL(x)  NTOHL(x)\r
+\r
+#define NTOHS(x)  (UINT16)((((UINT16) (x) & 0xff) << 8) | \\r
+                           (((UINT16) (x) & 0xff00) >> 8))\r
+\r
+#define HTONS(x)  NTOHS(x)\r
+#else\r
+#define NTOHL(x)  (UINT32)(x)\r
+#define HTONL(x)  (UINT32)(x)\r
+#define NTOHS(x)  (UINT16)(x)\r
+#define HTONS(x)  (UINT16)(x)\r
+#endif\r
+\r
+//\r
+// Test the IP's attribute, All the IPs are in host byte order.\r
+//\r
+#define IP4_IS_MULTICAST(Ip)              (((Ip) & 0xF0000000) == 0xE0000000)\r
+#define IP4_IS_LOCAL_BROADCAST(Ip)        ((Ip) == 0xFFFFFFFF)\r
+#define IP4_NET_EQUAL(Ip1, Ip2, NetMask)  (((Ip1) & (NetMask)) == ((Ip2) & (NetMask)))\r
+#define IP4_IS_VALID_NETMASK(Ip)          (NetGetMaskLength (Ip) != IP4_MASK_NUM)\r
+\r
+//\r
+// Convert the EFI_IP4_ADDRESS to plain UINT32 IP4 address.\r
+//\r
+#define EFI_IP4(EfiIpAddr)       (*(IP4_ADDR *) ((EfiIpAddr).Addr))\r
+#define EFI_NTOHL(EfiIp)         (NTOHL (EFI_IP4 ((EfiIp))))\r
+#define EFI_IP4_EQUAL(Ip1, Ip2)  (NetCompareMem (&(Ip1), &(Ip2), sizeof (EFI_IPv4_ADDRESS)) == 0)\r
+\r
+INTN\r
+NetGetMaskLength (\r
+  IN IP4_ADDR               Mask\r
+  );\r
+\r
+INTN\r
+NetGetIpClass (\r
+  IN IP4_ADDR               Addr\r
+  );\r
+\r
+BOOLEAN\r
+Ip4IsUnicast (\r
+  IN IP4_ADDR               Ip,\r
+  IN IP4_ADDR               NetMask\r
+  );\r
+\r
 extern IP4_ADDR mIp4AllMasks [IP4_MASK_NUM];
 
 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.
 //
 //
 // 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 (file)
index 0000000..53fe743
--- /dev/null
@@ -0,0 +1,350 @@
+/*++\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeDhcp4.h\r
+\r
+Abstract:\r
+  EFI PXE DHCPv4 protocol definition\r
+\r
+--*/\r
+\r
+#ifndef _PXEDHCP4_H_\r
+#define _PXEDHCP4_H_\r
+\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+//\r
+// PXE DHCPv4 GUID definition\r
+//\r
+\r
+#define EFI_PXE_DHCP4_PROTOCOL_GUID \\r
+  { 0x03c4e624, 0xac28, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x29, 0x3f, 0xc1, 0x4d } }\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+//\r
+// Interface definition\r
+//\r
+\r
+typedef struct _EFI_PXE_DHCP4_PROTOCOL EFI_PXE_DHCP4_PROTOCOL;\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+//\r
+// Descriptions of the DHCP version 4 header and options can be found\r
+// in RFC-2131 and RFC-2132 at www.ietf.org\r
+//\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+\r
+  UINT8 op;\r
+#define BOOTP_REQUEST   1\r
+#define BOOTP_REPLY   2\r
+\r
+  UINT8 htype;\r
+\r
+  UINT8 hlen;\r
+\r
+  UINT8 hops;\r
+\r
+  UINT32 xid;\r
+\r
+  UINT16 secs;\r
+#define DHCP4_INITIAL_SECONDS 4\r
+\r
+  UINT16 flags;\r
+#define DHCP4_BROADCAST_FLAG  0x8000\r
+\r
+  UINT32 ciaddr;\r
+\r
+  UINT32 yiaddr;\r
+\r
+  UINT32 siaddr;\r
+\r
+  UINT32 giaddr;\r
+\r
+  UINT8 chaddr[16];\r
+\r
+  UINT8 sname[64];\r
+\r
+  UINT8 fname[128];\r
+\r
+//\r
+// This is the minimum option length as specified in RFC-2131.\r
+// The packet must be padded out this far with DHCP4_PAD.\r
+// DHCPv4 packets are usually 576 bytes in length.  This length\r
+// includes the IPv4 and UDPv4 headers but not the media header.\r
+// Note: Not all DHCP relay agents will forward DHCPv4 packets\r
+// if they are less than 384 bytes or exceed 576 bytes.  Even if\r
+// the underlying hardware can handle smaller and larger packets,\r
+// many older relay agents will not accept them.\r
+//\r
+  UINT32 magik;\r
+#define DHCP4_MAGIK_NUMBER  0x63825363\r
+\r
+  UINT8 options[308];\r
+\r
+} DHCP4_HEADER;\r
+#pragma pack()\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+//\r
+// DHCPv4 packet definition.  Room for 576 bytes including IP and\r
+// UDP header.\r
+//\r
+\r
+#define DHCP4_MAX_PACKET_SIZE     576\r
+#define DHCP4_UDP_HEADER_SIZE     8\r
+#define DHCP4_IP_HEADER_SIZE      20\r
+\r
+#pragma pack(1)\r
+typedef union _DHCP4_PACKET {\r
+  UINT32 _force_data_alignment;\r
+\r
+  UINT8 raw[1500];\r
+\r
+  DHCP4_HEADER dhcp4;\r
+} DHCP4_PACKET;\r
+#pragma pack()\r
+\r
+#define DHCP4_SERVER_PORT 67\r
+#define DHCP4_CLIENT_PORT 68\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+//\r
+// DHCPv4 and PXE option numbers.\r
+//\r
+\r
+#define DHCP4_PAD                             0\r
+#define DHCP4_END                             255\r
+#define DHCP4_SUBNET_MASK                     1\r
+#define DHCP4_TIME_OFFSET                     2\r
+#define DHCP4_ROUTER_LIST                     3\r
+#define DHCP4_TIME_SERVERS                    4\r
+#define DHCP4_NAME_SERVERS                    5\r
+#define DHCP4_DNS_SERVERS                     6\r
+#define DHCP4_LOG_SERVERS                     7\r
+#define DHCP4_COOKIE_SERVERS                  8\r
+#define DHCP4_LPR_SREVERS                     9\r
+#define DHCP4_IMPRESS_SERVERS                 10\r
+#define DHCP4_RESOURCE_LOCATION_SERVERS       11\r
+#define DHCP4_HOST_NAME                       12\r
+#define DHCP4_BOOT_FILE_SIZE                  13\r
+#define DHCP4_DUMP_FILE                       14\r
+#define DHCP4_DOMAIN_NAME                     15\r
+#define DHCP4_SWAP_SERVER                     16\r
+#define DHCP4_ROOT_PATH                       17\r
+#define DHCP4_EXTENSION_PATH                  18\r
+#define DHCP4_IP_FORWARDING                   19\r
+#define DHCP4_NON_LOCAL_SOURCE_ROUTE          20\r
+#define DHCP4_POLICY_FILTER                   21\r
+#define DHCP4_MAX_DATAGRAM_SIZE               22\r
+#define DHCP4_DEFAULT_TTL                     23\r
+#define DHCP4_MTU_AGING_TIMEOUT               24\r
+#define DHCP4_MTU_SIZES                       25\r
+#define DHCP4_MTU_TO_USE                      26\r
+#define DHCP4_ALL_SUBNETS_LOCAL               27\r
+#define DHCP4_BROADCAST_ADDRESS               28\r
+#define DHCP4_PERFORM_MASK_DISCOVERY          29\r
+#define DHCP4_RESPOND_TO_MASK_REQ             30\r
+#define DHCP4_PERFORM_ROUTER_DISCOVERY        31\r
+#define DHCP4_ROUTER_SOLICIT_ADDRESS          32\r
+#define DHCP4_STATIC_ROUTER_LIST              33\r
+#define DHCP4_USE_ARP_TRAILERS                34\r
+#define DHCP4_ARP_CACHE_TIMEOUT               35\r
+#define DHCP4_ETHERNET_ENCAPSULATION          36\r
+#define DHCP4_TCP_DEFAULT_TTL                 37\r
+#define DHCP4_TCP_KEEP_ALIVE_INT              38\r
+#define DHCP4_KEEP_ALIVE_GARBAGE              39\r
+#define DHCP4_NIS_DOMAIN_NAME                 40\r
+#define DHCP4_NIS_SERVERS                     41\r
+#define DHCP4_NTP_SERVERS                     42\r
+#define DHCP4_VENDOR_SPECIFIC                 43\r
+# define PXE_MTFTP_IP                         1\r
+# define PXE_MTFTP_CPORT                      2\r
+# define PXE_MTFTP_SPORT                      3\r
+# define PXE_MTFTP_TMOUT                      4\r
+# define PXE_MTFTP_DELAY                      5\r
+# define PXE_DISCOVERY_CONTROL                6\r
+#  define PXE_DISABLE_BROADCAST_DISCOVERY     0x01\r
+#  define PXE_DISABLE_MULTICAST_DISCOVERY     0x02\r
+#  define PXE_ACCEPT_ONLY_PXE_BOOT_SERVERS    0x04\r
+#  define PXE_DO_NOT_PROMPT                   0x08\r
+# define PXE_DISCOVERY_MCAST_ADDR             7\r
+# define PXE_BOOT_SERVERS                     8\r
+# define PXE_BOOT_MENU                        9\r
+# define PXE_BOOT_PROMPT                      10\r
+# define PXE_MCAST_ADDRS_ALLOC                11\r
+# define PXE_CREDENTIAL_TYPES                 12\r
+# define PXE_BOOT_ITEM                        71\r
+#define DHCP4_NBNS_SERVERS                    44\r
+#define DHCP4_NBDD_SERVERS                    45\r
+#define DHCP4_NETBIOS_NODE_TYPE               46\r
+#define DHCP4_NETBIOS_SCOPE                   47\r
+#define DHCP4_XWINDOW_SYSTEM_FONT_SERVERS     48\r
+#define DHCP4_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49\r
+#define DHCP4_REQUESTED_IP_ADDRESS            50\r
+#define DHCP4_LEASE_TIME                      51\r
+#define DHCP4_OPTION_OVERLOAD                 52\r
+# define DHCP4_OVERLOAD_FNAME                 1\r
+# define DHCP4_OVERLOAD_SNAME                 2\r
+# define DHCP4_OVERLOAD_FNAME_AND_SNAME       3\r
+#define DHCP4_MESSAGE_TYPE                    53\r
+# define DHCP4_MESSAGE_TYPE_DISCOVER          1\r
+# define DHCP4_MESSAGE_TYPE_OFFER             2\r
+# define DHCP4_MESSAGE_TYPE_REQUEST           3\r
+# define DHCP4_MESSAGE_TYPE_DECLINE           4\r
+# define DHCP4_MESSAGE_TYPE_ACK               5\r
+# define DHCP4_MESSAGE_TYPE_NAK               6\r
+# define DHCP4_MESSAGE_TYPE_RELEASE           7\r
+# define DHCP4_MESSAGE_TYPE_INFORM            8\r
+#define DHCP4_SERVER_IDENTIFIER               54\r
+#define DHCP4_PARAMETER_REQUEST_LIST          55\r
+#define DHCP4_ERROR_MESSAGE                   56\r
+#define DHCP4_MAX_MESSAGE_SIZE                57\r
+# define DHCP4_DEFAULT_MAX_MESSAGE_SIZE       576\r
+#define DHCP4_RENEWAL_TIME                    58\r
+#define DHCP4_REBINDING_TIME                  59\r
+#define DHCP4_CLASS_IDENTIFIER                60\r
+#define DHCP4_CLIENT_IDENTIFIER               61\r
+#define DHCP4_NISPLUS_DOMAIN_NAME             64\r
+#define DHCP4_NISPLUS_SERVERS                 65\r
+#define DHCP4_TFTP_SERVER_NAME                66\r
+#define DHCP4_BOOTFILE                        67\r
+#define DHCP4_MOBILE_IP_HOME_AGENTS           68\r
+#define DHCP4_SMPT_SERVERS                    69\r
+#define DHCP4_POP3_SERVERS                    70\r
+#define DHCP4_NNTP_SERVERS                    71\r
+#define DHCP4_WWW_SERVERS                     72\r
+#define DHCP4_FINGER_SERVERS                  73\r
+#define DHCP4_IRC_SERVERS                     74\r
+#define DHCP4_STREET_TALK_SERVERS             75\r
+#define DHCP4_STREET_TALK_DIR_ASSIST_SERVERS  76\r
+#define DHCP4_NDS_SERVERS                     85\r
+#define DHCP4_NDS_TREE_NAME                   86\r
+#define DHCP4_NDS_CONTEXT                     87\r
+#define DHCP4_SYSTEM_ARCHITECTURE             93\r
+#define DHCP4_NETWORK_ARCHITECTURE            94\r
+#define DHCP4_PLATFORM_ID                     97\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+//\r
+// DHCP4 option format.\r
+//\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT8 op;\r
+  UINT8 len;\r
+  UINT8 data[1];\r
+} DHCP4_OP;\r
+#pragma pack()\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+typedef struct {\r
+  DHCP4_PACKET Discover;\r
+  DHCP4_PACKET Offer;\r
+  DHCP4_PACKET Request;\r
+  DHCP4_PACKET AckNak;\r
+  BOOLEAN SetupCompleted;\r
+  BOOLEAN InitCompleted;\r
+  BOOLEAN SelectCompleted;\r
+  BOOLEAN IsBootp;\r
+  BOOLEAN IsAck;\r
+} EFI_PXE_DHCP4_DATA;\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_PXE_DHCP4_RUN) (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN OPTIONAL UINTN         OpLen,\r
+  IN OPTIONAL VOID          *OpList\r
+  );\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_PXE_DHCP4_SETUP) (\r
+  IN EFI_PXE_DHCP4_PROTOCOL          *This,\r
+  IN OPTIONAL EFI_PXE_DHCP4_DATA     * NewData\r
+  );\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_PXE_DHCP4_INIT) (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN UINTN                  SecondsTimeout,\r
+  OUT UINTN                 *Offers,\r
+  OUT DHCP4_PACKET          **OfferList\r
+  );\r
+\r
+#define DHCP4_MIN_SECONDS   1\r
+#define DHCP4_MAX_SECONDS   60\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_PXE_DHCP4_SELECT) (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN UINTN                  SecondsTimeout,\r
+  IN DHCP4_PACKET           * offer\r
+  );\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_PXE_DHCP4_RENEW) (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  UINTN                     seconds_timeout\r
+  );\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_PXE_DHCP4_REBIND) (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  UINTN                     seconds_timeout\r
+  );\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_PXE_DHCP4_RELEASE) (\r
+  IN EFI_PXE_DHCP4_PROTOCOL * This\r
+  );\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+#define EFI_PXE_DHCP4_PROTOCOL_REVISION    0x00010000\r
+\r
+struct _EFI_PXE_DHCP4_PROTOCOL {\r
+  UINT64 Revision;\r
+  EFI_PXE_DHCP4_RUN Run;\r
+  EFI_PXE_DHCP4_SETUP Setup;\r
+  EFI_PXE_DHCP4_INIT Init;\r
+  EFI_PXE_DHCP4_SELECT Select;\r
+  EFI_PXE_DHCP4_RENEW Renew;\r
+  EFI_PXE_DHCP4_REBIND Rebind;\r
+  EFI_PXE_DHCP4_RELEASE Release;\r
+  EFI_PXE_DHCP4_DATA *Data;\r
+};\r
+\r
+//\r
+//\r
+//\r
+\r
+extern EFI_GUID gEfiPxeDhcp4ProtocolGuid;\r
+\r
+#endif /* _PXEDHCP4_H_ */\r
+/* EOF - PxeDhcp4.h */\r
diff --git a/MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h b/MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h
new file mode 100644 (file)
index 0000000..cfba380
--- /dev/null
@@ -0,0 +1,85 @@
+/*++\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeDhcp4Callback.h\r
+\r
+Abstract:\r
+  EFI PXE DHCP4 Callback protocol definition.\r
+\r
+--*/\r
+\r
+#ifndef _PXE_DHCP4CALLBACK_H\r
+#define _PXE_DHCP4CALLBACK_H\r
+\r
+#include <Protocol/PxeDhcp4.h>\r
+//\r
+// GUID definition\r
+//\r
+\r
+#define EFI_PXE_DHCP4_CALLBACK_PROTOCOL_GUID \\r
+{ 0xc1544c01, 0x92a4, 0x4198, {0x8a, 0x84, 0x77, 0x85, 0x83, 0xc2, 0x36, 0x21 } }\r
+\r
+\r
+//\r
+// Revision number\r
+//\r
+\r
+#define EFI_PXE_DHCP4_CALLBACK_INTERFACE_REVISION   0x00010000\r
+\r
+//\r
+// Interface definition\r
+//\r
+\r
+typedef struct _EFI_PXE_DHCP4_CALLBACK_PROTOCOL EFI_PXE_DHCP4_CALLBACK_PROTOCOL;\r
+\r
+typedef enum {\r
+  EFI_PXE_DHCP4_FUNCTION_FIRST,\r
+  EFI_PXE_DHCP4_FUNCTION_INIT,\r
+  EFI_PXE_DHCP4_FUNCTION_SELECT,\r
+  EFI_PXE_DHCP4_FUNCTION_RENEW,\r
+  EFI_PXE_DHCP4_FUNCTION_REBIND,\r
+  EFI_PXE_DHCP4_FUNCTION_LAST\r
+} EFI_PXE_DHCP4_FUNCTION;\r
+\r
+typedef enum {\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS_FIRST,\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS_ABORT,\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_ABORT,\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_ABORT,\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE,\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_CONTINUE,\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE,\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS_LAST\r
+} EFI_PXE_DHCP4_CALLBACK_STATUS;\r
+\r
+typedef\r
+EFI_PXE_DHCP4_CALLBACK_STATUS\r
+(EFIAPI *EFI_PXE_DHCP4_CALLBACK) (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN EFI_PXE_DHCP4_FUNCTION Function,\r
+  IN UINT32                 PacketLen,\r
+  IN DHCP4_PACKET           *Packet OPTIONAL\r
+  );\r
+\r
+struct _EFI_PXE_DHCP4_CALLBACK_PROTOCOL {\r
+  UINT64                      Revision;\r
+  EFI_PXE_DHCP4_CALLBACK      Callback;\r
+};\r
+\r
+//\r
+// GUID declaration\r
+//\r
+\r
+extern EFI_GUID gEfiPxeDhcp4CallbackProtocolGuid;\r
+\r
+#endif /* _PXE_DHCP4CALLBACK_H */\r
+/* EOF - PxeDhcp4Callback.h */\r
diff --git a/MdeModulePkg/Include/Protocol/Tcp.h b/MdeModulePkg/Include/Protocol/Tcp.h
new file mode 100644 (file)
index 0000000..f49915c
--- /dev/null
@@ -0,0 +1,108 @@
+/*++\r
+\r
+Copyright (c) 2004, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+    tcp.h\r
+\r
+Abstract:\r
+\r
+    EFI Transmission Control Protocol\r
+\r
+\r
+\r
+Revision History\r
+\r
+--*/\r
+\r
+\r
+#ifndef _EFITCP_H\r
+#define _EFITCP_H\r
+\r
+\r
+#include <Protocol/PxeBaseCode.h>\r
+\r
+//\r
+// PXE Base Code protocol\r
+//\r
+\r
+#define EFI_TCP_PROTOCOL_GUID \\r
+    { 0x02b3d5f2, 0xac28, 0x11d3, { 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }}\r
+\r
+\r
+typedef UINT16 EFI_PXE_BASE_CODE_TCP_PORT;\r
+\r
+//\r
+// Port Receive Filter definitions\r
+//\r
+#define EFI_PXE_BASE_CODE_MAX_PORTCNT             8\r
+typedef struct {\r
+    UINT8                       Filters;\r
+    UINT8                       IpCnt;\r
+    UINT16                      reserved;\r
+    EFI_IP_ADDRESS              IpList[EFI_PXE_BASE_CODE_MAX_PORTCNT];\r
+} EFI_TCP_PORT_FILTER;\r
+\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_TCP_WRITE) (\r
+    IN EFI_PXE_BASE_CODE_PROTOCOL        *This,\r
+    IN UINT16                                    OpFlags,\r
+    IN UINT16                                    *UrgentPointer,\r
+    IN UINT32                                    *SequenceNumber,\r
+    IN UINT32                                    *AckNumber,\r
+    IN UINT16                                    *HlenResCode,\r
+    IN UINT16                                    *Window,\r
+    IN EFI_IP_ADDRESS                            *DestIp,\r
+    IN UINT16                                    *DestPort,\r
+    IN EFI_IP_ADDRESS                            *GatewayIp,  OPTIONAL\r
+    IN EFI_IP_ADDRESS                            *SrcIp,      OPTIONAL\r
+    IN UINT16                                    *SrcPort,    OPTIONAL\r
+    IN UINTN                                     *HeaderSize, OPTIONAL\r
+    IN VOID                                      *HeaderPtr,  OPTIONAL\r
+    IN UINTN                                     *BufferSize,\r
+    IN VOID                                      *BufferPtr\r
+    );\r
+\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_TCP_READ) (\r
+    IN EFI_PXE_BASE_CODE_PROTOCOL        *This,\r
+    IN UINT16                                    OpFlags,\r
+    IN OUT EFI_IP_ADDRESS                        *DestIp,      OPTIONAL\r
+    IN OUT UINT16                                *DestPort,    OPTIONAL\r
+    IN OUT EFI_IP_ADDRESS                        *SrcIp,       OPTIONAL\r
+    IN OUT UINT16                                *SrcPort,     OPTIONAL\r
+    IN UINTN                                     *HeaderSize,  OPTIONAL\r
+    IN VOID                                      *HeaderPtr,   OPTIONAL\r
+    IN OUT UINTN                                 *BufferSize,\r
+    IN VOID                                      *BufferPtr\r
+    );\r
+\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *EFI_TCP_SET_PORT_FILTER) (\r
+    IN EFI_PXE_BASE_CODE_PROTOCOL    *This,\r
+    IN EFI_TCP_PORT_FILTER                   *NewFilter\r
+    );\r
+\r
+//\r
+// TCP Protocol structure\r
+//\r
+typedef struct _EFI_TCP_PROTOCOL {\r
+    EFI_TCP_WRITE             TcpWrite;\r
+    EFI_TCP_READ              TcpRead;\r
+    EFI_TCP_SET_PORT_FILTER   SetPortFilter;\r
+} EFI_TCP_PROTOCOL;\r
+\r
+extern EFI_GUID gEfiTcpProtocolGuid;\r
+\r
+#endif /* _EFITCP_H */\r
index dcf2309c71c4d470364df9d1cb226870e9464cb8..9057a03a67a91de5c225fe463491caeb706a3f49 100644 (file)
@@ -77,6 +77,7 @@ IP4_ADDR  mIp4AllMasks[IP4_MASK_NUM] = {
   0xFFFFFFFF,
 };
 
   0xFFFFFFFF,
 };
 
+EFI_IPv4_ADDRESS  mZeroIp4Addr = {0, 0, 0, 0};
 
 /**\r
   Converts the low nibble of a byte  to hex unicode character.\r
 
 /**\r
   Converts the low nibble of a byte  to hex unicode character.\r
index 72f58a1fb68c25e78aa9443720e9f55500a451d6..e6a93479694714f92dd8ed7eb49ae49747ce5fac 100644 (file)
   gEfiNicIp4ConfigProtocolGuid = {0xdca3d4d, 0x12da, 0x4728, { 0xbf, 0x7e, 0x86, 0xce, 0xb9, 0x28, 0xd0, 0x67 }}\r
   gEfiNicIp4ConfigVariableGuid = {0xd8944553, 0xc4dd, 0x41f4, { 0x9b, 0x30, 0xe1, 0x39, 0x7c, 0xfb, 0x26, 0x7b }}\r
 \r
   gEfiNicIp4ConfigProtocolGuid = {0xdca3d4d, 0x12da, 0x4728, { 0xbf, 0x7e, 0x86, 0xce, 0xb9, 0x28, 0xd0, 0x67 }}\r
   gEfiNicIp4ConfigVariableGuid = {0xd8944553, 0xc4dd, 0x41f4, { 0x9b, 0x30, 0xe1, 0x39, 0x7c, 0xfb, 0x26, 0x7b }}\r
 \r
+  gEfiTcpProtocolGuid = { 0x02b3d5f2, 0xac28, 0x11d3, { 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }}\r
+  gEfiPxeDhcp4CallbackProtocolGuid = { 0xc1544c01, 0x92a4, 0x4198, {0x8a, 0x84, 0x77, 0x85, 0x83, 0xc2, 0x36, 0x21 } }\r
+  gEfiPxeDhcp4ProtocolGuid = { 0x03c4e624, 0xac28, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x29, 0x3f, 0xc1, 0x4d } }\r
+\r
 [Ppis.common]\r
   gPeiBaseMemoryTestPpiGuid      = { 0xB6EC423C, 0x21D2, 0x490D, { 0x85, 0xC6, 0xDD, 0x58, 0x64, 0xEA, 0xA6, 0x74 }}\r
 \r
 [Ppis.common]\r
   gPeiBaseMemoryTestPpiGuid      = { 0xB6EC423C, 0x21D2, 0x490D, { 0x85, 0xC6, 0xDD, 0x58, 0x64, 0xEA, 0xA6, 0x74 }}\r
 \r
index be390f443f66d2f46fd6cdc4fcb437fbc6f88398..90a97c18a65b61917fa2a84e0817cdb2289ef7d5 100644 (file)
@@ -55,7 +55,7 @@
 [LibraryClasses.IPF]\r
   IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf\r
 \r
 [LibraryClasses.IPF]\r
   IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf\r
 \r
-[LibraryClasses.EBC.DXE_RUNTIME_DRIVER]\r
+[LibraryClasses.EBC]\r
   IoLib|IntelFrameworkPkg/Library/DxeIoLibCpuIo/DxeIoLibCpuIo.inf\r
 \r
 [LibraryClasses.common.PEI_CORE]\r
   IoLib|IntelFrameworkPkg/Library/DxeIoLibCpuIo/DxeIoLibCpuIo.inf\r
 \r
 [LibraryClasses.common.PEI_CORE]\r
   UsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf\r
   NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf\r
   IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf\r
   UsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf\r
   NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf\r
   IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf\r
+  UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
 \r
 [LibraryClasses.common.DXE_RUNTIME_DRIVER]\r
   HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf\r
 \r
 [LibraryClasses.common.DXE_RUNTIME_DRIVER]\r
   HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf\r
   MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf\r
   MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
 \r
   MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf\r
   MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
 \r
+  MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf\r
+  MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf\r
   MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDxe.inf\r
   MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDxe.inf\r
+  MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf\r
   MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf\r
   MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf\r
+  MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf\r
+  MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf\r
   MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf\r
   MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf\r
   MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf\r
   MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf\r
   MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf\r
   MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf\r
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h
new file mode 100644 (file)
index 0000000..05f542d
--- /dev/null
@@ -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 (file)
index 0000000..2c10857
--- /dev/null
@@ -0,0 +1,763 @@
+/** @file\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  ArpDriver.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+\r
+#include "ArpDriver.h"\r
+#include "ArpImpl.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gArpDriverBinding = {\r
+  ArpDriverBindingSupported,\r
+  ArpDriverBindingStart,\r
+  ArpDriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+\r
+/**\r
+  Create and initialize the arp service context data.\r
+\r
+  @param  ImageHandle            The image handle representing the loaded driver\r
+                                 image.\r
+  @param  ControllerHandle       The controller handle the driver binds to.\r
+  @param  ArpService             Pointer to the buffer containing the arp service\r
+                                 context data.\r
+\r
+  @retval EFI_SUCCESS            The arp service context is initialized.\r
+  @retval other                  Failed to initialize the arp service context.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+ArpCreateService (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_HANDLE        ControllerHandle,\r
+  IN ARP_SERVICE_DATA  *ArpService\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  ASSERT (ArpService != NULL);\r
+\r
+  ArpService->Signature = ARP_SERVICE_DATA_SIGNATURE;\r
+\r
+  //\r
+  // Init the servicebinding protocol members.\r
+  //\r
+  ArpService->ServiceBinding.CreateChild  = ArpServiceBindingCreateChild;\r
+  ArpService->ServiceBinding.DestroyChild = ArpServiceBindingDestroyChild;\r
+\r
+  //\r
+  // Save the handles.\r
+  //\r
+  ArpService->ImageHandle      = ImageHandle;\r
+  ArpService->ControllerHandle = ControllerHandle;\r
+\r
+  //\r
+  // Create a MNP child instance.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             ImageHandle,\r
+             &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+             &ArpService->MnpChildHandle\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Open the MNP protocol.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ArpService->MnpChildHandle,\r
+                  &gEfiManagedNetworkProtocolGuid,\r
+                  (VOID **)&ArpService->Mnp,\r
+                  ImageHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_EXIT;\r
+  }\r
+\r
+  //\r
+  // Get the underlayer Snp mode data.\r
+  //\r
+  Status = ArpService->Mnp->GetModeData (ArpService->Mnp, NULL, &ArpService->SnpMode);\r
+  if ((Status != EFI_NOT_STARTED) && EFI_ERROR (Status)) {\r
+    goto ERROR_EXIT;\r
+  }\r
+\r
+  if (ArpService->SnpMode.IfType != NET_IFTYPE_ETHERNET) {\r
+    //\r
+    // Only support the ethernet.\r
+    //\r
+    Status = EFI_UNSUPPORTED;\r
+    goto ERROR_EXIT;\r
+  }\r
+\r
+  //\r
+  // Set the Mnp config parameters.\r
+  //\r
+  ArpService->MnpConfigData.ReceivedQueueTimeoutValue = 0;\r
+  ArpService->MnpConfigData.TransmitQueueTimeoutValue = 0;\r
+  ArpService->MnpConfigData.ProtocolTypeFilter        = ARP_ETHER_PROTO_TYPE;\r
+  ArpService->MnpConfigData.EnableUnicastReceive      = TRUE;\r
+  ArpService->MnpConfigData.EnableMulticastReceive    = FALSE;\r
+  ArpService->MnpConfigData.EnableBroadcastReceive    = TRUE;\r
+  ArpService->MnpConfigData.EnablePromiscuousReceive  = FALSE;\r
+  ArpService->MnpConfigData.FlushQueuesOnReset        = TRUE;\r
+  ArpService->MnpConfigData.EnableReceiveTimestamps   = FALSE;\r
+  ArpService->MnpConfigData.DisableBackgroundPolling  = FALSE;\r
+\r
+  //\r
+  // Configure the Mnp child.\r
+  //\r
+  Status = ArpService->Mnp->Configure (ArpService->Mnp, &ArpService->MnpConfigData);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_EXIT;\r
+  }\r
+\r
+  //\r
+  // Create the event used in the RxToken.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  NET_TPL_EVENT,\r
+                  ArpOnFrameRcvd,\r
+                  ArpService,\r
+                  &ArpService->RxToken.Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_EXIT;\r
+  }\r
+\r
+  //\r
+  // Create the Arp heartbeat timer.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+                  NET_TPL_TIMER,\r
+                  ArpTimerHandler,\r
+                  ArpService,\r
+                  &ArpService->PeriodicTimer\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_EXIT;\r
+  }\r
+\r
+  //\r
+  // Start the heartbeat timer.\r
+  //\r
+  Status = gBS->SetTimer (\r
+                  ArpService->PeriodicTimer,\r
+                  TimerPeriodic,\r
+                  ARP_PERIODIC_TIMER_INTERVAL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_EXIT;\r
+  }\r
+\r
+  //\r
+  // Init the lock.\r
+  //\r
+  NET_LOCK_INIT (&ArpService->Lock);\r
+\r
+  //\r
+  // Init the lists.\r
+  //\r
+  NetListInit (&ArpService->ChildrenList);\r
+  NetListInit (&ArpService->PendingRequestTable);\r
+  NetListInit (&ArpService->DeniedCacheTable);\r
+  NetListInit (&ArpService->ResolvedCacheTable);\r
+\r
+ERROR_EXIT:\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Clean the arp service context data.\r
+\r
+  @param  ArpService             Pointer to the buffer containing the arp service\r
+                                 context data.\r
+\r
+  @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+ArpCleanService (\r
+  IN ARP_SERVICE_DATA  *ArpService\r
+  )\r
+{\r
+  NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);\r
+\r
+  if (ArpService->PeriodicTimer != NULL) {\r
+    //\r
+    // Cancle and close the PeriodicTimer.\r
+    //\r
+    gBS->SetTimer (ArpService->PeriodicTimer, TimerCancel, 0);\r
+    gBS->CloseEvent (ArpService->PeriodicTimer);\r
+  }\r
+\r
+  if (ArpService->RxToken.Event != NULL) {\r
+    //\r
+    // Cancle the RxToken and close the event in the RxToken.\r
+    //\r
+    ArpService->Mnp->Cancel (ArpService->Mnp, NULL);\r
+    gBS->CloseEvent (ArpService->RxToken.Event);\r
+  }\r
+\r
+  if (ArpService->Mnp != NULL) {\r
+    //\r
+    // Reset the Mnp child and close the Mnp protocol.\r
+    //\r
+    ArpService->Mnp->Configure (ArpService->Mnp, NULL);\r
+    gBS->CloseProtocol (\r
+           ArpService->MnpChildHandle,\r
+           &gEfiManagedNetworkProtocolGuid,\r
+           ArpService->ImageHandle,\r
+           ArpService->ControllerHandle\r
+           );\r
+  }\r
+\r
+  if (ArpService->MnpChildHandle != NULL) {\r
+    //\r
+    // Destroy the mnp child.\r
+    //\r
+    NetLibDestroyServiceChild(\r
+      ArpService->ControllerHandle,\r
+      ArpService->ImageHandle,\r
+      &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+      ArpService->MnpChildHandle\r
+      );\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to test.\r
+  @param  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                 device to start.\r
+\r
+  @retval EFI_SUCCES             This driver supports this device\r
+  @retval EFI_ALREADY_STARTED    This driver is already running on this device.\r
+  @retval other                  This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // Test to see if Arp SB is already installed.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiArpServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (Status == EFI_SUCCESS) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  //\r
+  // Test to see if MNP SB is installed.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to bind driver to\r
+  @param  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                 device to start.\r
+\r
+  @retval EFI_SUCCES             This driver is added to ControllerHandle\r
+  @retval EFI_ALREADY_STARTED    This driver is already running on ControllerHandle\r
+  @retval other                  This driver does not support this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  ARP_SERVICE_DATA  *ArpService;\r
+\r
+  //\r
+  // Allocate a zero pool for ArpService.\r
+  //\r
+  ArpService = NetAllocateZeroPool (sizeof(ARP_SERVICE_DATA));\r
+  if (ArpService == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Initialize the arp service context data.\r
+  //\r
+  Status = ArpCreateService (This->DriverBindingHandle, ControllerHandle, ArpService);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR;\r
+  }\r
+\r
+  //\r
+  // Install the ARP service binding protocol.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &ControllerHandle,\r
+                  &gEfiArpServiceBindingProtocolGuid,\r
+                  &ArpService->ServiceBinding,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR;\r
+  }\r
+\r
+  //\r
+  // OK, start to receive arp packets from Mnp.\r
+  //\r
+  Status = ArpService->Mnp->Receive (ArpService->Mnp, &ArpService->RxToken);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR;\r
+  }\r
+\r
+  return Status;\r
+\r
+ERROR:\r
+\r
+  //\r
+  // On error, clean the arp service context data, and free the memory allocated.\r
+  //\r
+  ArpCleanService (ArpService);\r
+  NetFreePool (ArpService);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to stop driver on\r
+  @param  NumberOfChildren       Number of Handles in ChildHandleBuffer. If number\r
+                                 of  children is zero stop the entire bus driver.\r
+  @param  ChildHandleBuffer      List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCES             This driver is removed ControllerHandle\r
+  @retval other                  This driver was not removed from this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpDriverBindingStop (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN UINTN                        NumberOfChildren,\r
+  IN EFI_HANDLE                   *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  EFI_HANDLE                    NicHandle;\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  ARP_SERVICE_DATA              *ArpService;\r
+  ARP_INSTANCE_DATA             *Instance;\r
+\r
+  //\r
+  // Get the NicHandle which the arp servicebinding is installed on.\r
+  //\r
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);\r
+  if (NicHandle == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Try to get the arp servicebinding protocol on the NicHandle.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  &gEfiArpServiceBindingProtocolGuid,\r
+                  (VOID **)&ServiceBinding,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    ARP_DEBUG_ERROR (("ArpDriverBindingStop: Open ArpSb failed, %r.\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  ArpService = ARP_SERVICE_DATA_FROM_THIS (ServiceBinding);\r
+\r
+  while (!NetListIsEmpty (&ArpService->ChildrenList)) {\r
+    //\r
+    // Iterate all the instances.\r
+    //\r
+    Instance = NET_LIST_HEAD (&ArpService->ChildrenList, ARP_INSTANCE_DATA, List);\r
+\r
+    //\r
+    // Destroy this arp child.\r
+    //\r
+    ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);\r
+  }\r
+\r
+  ASSERT (NetListIsEmpty (&ArpService->PendingRequestTable));\r
+  ASSERT (NetListIsEmpty (&ArpService->DeniedCacheTable));\r
+  ASSERT (NetListIsEmpty (&ArpService->ResolvedCacheTable));\r
+\r
+  //\r
+  // Uninstall the ARP ServiceBinding protocol.\r
+  //\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  NicHandle,\r
+                  &gEfiArpServiceBindingProtocolGuid,\r
+                  &ArpService->ServiceBinding,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    ARP_DEBUG_ERROR (("ArpDriverBindingStop: Failed to uninstall ArpSb, %r.\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Clean the arp servicebinding context data and free the memory allocated.\r
+  //\r
+  ArpCleanService (ArpService);\r
+  NetFreePool (ArpService);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Creates a child handle with a set of I/O services.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ChildHandle            Pointer to the handle of the child to create. If\r
+                                 it is NULL, then a new handle is created. If it is\r
+                                 not NULL, then the I/O services are  added to the\r
+                                 existing child handle.\r
+\r
+  @retval EFI_SUCCES             The child handle was created with the I/O\r
+                                 services.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources availabe to create\r
+                                 the child.\r
+  @retval other                  The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpServiceBindingCreateChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    *ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  ARP_SERVICE_DATA   *ArpService;\r
+  ARP_INSTANCE_DATA  *Instance;\r
+  VOID               *Mnp;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ArpService = ARP_SERVICE_DATA_FROM_THIS (This);\r
+\r
+  //\r
+  // Allocate memory for the instance context data.\r
+  //\r
+  Instance = NetAllocateZeroPool (sizeof(ARP_INSTANCE_DATA));\r
+  if (Instance == NULL) {\r
+    ARP_DEBUG_ERROR (("ArpSBCreateChild: Failed to allocate memory for Instance.\n"));\r
+\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Init the instance context data.\r
+  //\r
+  ArpInitInstance (ArpService, Instance);\r
+\r
+  //\r
+  // Install the ARP protocol onto the ChildHandle.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiArpProtocolGuid,\r
+                  (VOID *)&Instance->ArpProto,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    ARP_DEBUG_ERROR (("ArpSBCreateChild: faild to install ARP protocol, %r.\n", Status));\r
+\r
+    NetFreePool (Instance);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Save the ChildHandle.\r
+  //\r
+  Instance->Handle = *ChildHandle;\r
+\r
+  //\r
+  // Open the Managed Network protocol BY_CHILD.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ArpService->MnpChildHandle,\r
+                  &gEfiManagedNetworkProtocolGuid,\r
+                  (VOID **) &Mnp,\r
+                  gArpDriverBinding.DriverBindingHandle,\r
+                  Instance->Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR;\r
+  }\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ERROR;\r
+  }\r
+\r
+  //\r
+  // Insert the instance into children list managed by the arp service context data.\r
+  //\r
+  NetListInsertTail (&ArpService->ChildrenList, &Instance->List);\r
+  ArpService->ChildrenNumber++;\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+ERROR:\r
+\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    gBS->CloseProtocol (\r
+           ArpService->MnpChildHandle,\r
+           &gEfiManagedNetworkProtocolGuid,\r
+           gArpDriverBinding.DriverBindingHandle,\r
+           Instance->Handle\r
+           );\r
+\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Instance->Handle,\r
+           &gEfiArpProtocolGuid,\r
+           &Instance->ArpProto,\r
+           NULL\r
+           );\r
+\r
+    //\r
+    // Free the allocated memory.\r
+    //\r
+    NetFreePool (Instance);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Destroys a child handle with a set of I/O services.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ChildHandle            Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES             The I/O services were removed from the child\r
+                                 handle.\r
+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services\r
+                                  that are being removed.\r
+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.\r
+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because\r
+                                 its  I/O services are being used.\r
+  @retval other                  The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  ARP_SERVICE_DATA   *ArpService;\r
+  ARP_INSTANCE_DATA  *Instance;\r
+  EFI_ARP_PROTOCOL   *Arp;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ArpService = ARP_SERVICE_DATA_FROM_THIS (This);\r
+\r
+  //\r
+  // Get the arp protocol.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiArpProtocolGuid,\r
+                  (VOID **)&Arp,\r
+                  ArpService->ImageHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Instance = ARP_INSTANCE_DATA_FROM_THIS (Arp);\r
+\r
+  if (Instance->Destroyed) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Use the Destroyed as a flag to avoid re-entrance.\r
+  //\r
+  Instance->Destroyed = TRUE;\r
+\r
+  //\r
+  // Close the Managed Network protocol.\r
+  //\r
+  gBS->CloseProtocol (\r
+         ArpService->MnpChildHandle,\r
+         &gEfiManagedNetworkProtocolGuid,\r
+         gArpDriverBinding.DriverBindingHandle,\r
+         ChildHandle\r
+         );\r
+\r
+  //\r
+  // Uninstall the ARP protocol.\r
+  //\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiArpProtocolGuid,\r
+                  &Instance->ArpProto,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    ARP_DEBUG_ERROR (("ArpSBDestroyChild: Failed to uninstall the arp protocol, %r.\n",\r
+      Status));\r
+\r
+    Instance->Destroyed = FALSE;\r
+    return Status;\r
+  }\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    Instance->Destroyed = FALSE;\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (Instance->Configured) {\r
+    //\r
+    // Delete the related cache entry.\r
+    //\r
+    ArpDeleteCacheEntry (Instance, FALSE, NULL, TRUE);\r
+\r
+    //\r
+    // Reset the instance configuration.\r
+    //\r
+    ArpConfigureInstance (Instance, NULL);\r
+  }\r
+\r
+  //\r
+  // Remove this instance from the ChildrenList.\r
+  //\r
+  NetListRemoveEntry (&Instance->List);\r
+  ArpService->ChildrenNumber--;\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+  NetFreePool (Instance);\r
+\r
+  return Status;\r
+}\r
+\r
+//@MT: EFI_DRIVER_ENTRY_POINT (ArpDriverEntryPoint)\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+ArpDriverEntryPoint (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  The entry point for Arp driver which installs the driver binding and component name\r
+  protocol on its ImageHandle.\r
+\r
+Arguments:\r
+\r
+  ImageHandle - The image handle of the driver.\r
+  SystemTable - The system table.\r
+\r
+Returns:\r
+\r
+  EFI_SUCCESS - if the driver binding and component name protocols are successfully\r
+                installed, otherwise if failed.\r
+\r
+--*/\r
+{\r
+  return NetLibInstallAllDriverProtocols (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gArpDriverBinding,\r
+           ImageHandle,\r
+           &gArpComponentName,\r
+           NULL,\r
+           NULL\r
+           );\r
+}\r
+\r
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h
new file mode 100644 (file)
index 0000000..93526c7
--- /dev/null
@@ -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_
+
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/Arp.h>\r
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/ServiceBinding.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#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 (file)
index 0000000..4cc9b2e
--- /dev/null
@@ -0,0 +1,59 @@
+#/** @file\r
+# Component description file for ARP module\r
+#\r
+# Copyright (c) 2006, Intel Corporation\r
+#\r
+#   All rights reserved. This program and the accompanying materials\r
+#   are licensed and made available under the terms and conditions of the BSD License\r
+#   which accompanies this distribution.  The full text of the license may be found at\r
+#   http://opensource.org/licenses/bsd-license.php\r
+#\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = ArpDxe\r
+  FILE_GUID                      = 529D3F93-E8E9-4e73-B1E1-BDF6A9D50113\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  EDK_RELEASE_VERSION            = 0x00020000\r
+  EFI_SPECIFICATION_VERSION      = 0x00020000\r
+\r
+  ENTRY_POINT                    = ArpDriverEntryPoint\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources.common]\r
+  ArpMain.c\r
+  ArpDriver.h\r
+  ComponentName.c\r
+  ArpImpl.h\r
+  ArpImpl.c\r
+  ArpDebug.h\r
+  ArpDriver.c\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  UefiLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  DebugLib\r
+  NetLib\r
+\r
+\r
+[Protocols]\r
+  gEfiManagedNetworkServiceBindingProtocolGuid  # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiArpServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiManagedNetworkProtocolGuid                # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiArpProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED\r
+\r
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa
new file mode 100644 (file)
index 0000000..89178fa
--- /dev/null
@@ -0,0 +1,72 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+  <MsaHeader>\r
+    <ModuleName>Arp</ModuleName>\r
+    <ModuleType>DXE_DRIVER</ModuleType>\r
+    <GuidValue>529D3F93-E8E9-4e73-B1E1-BDF6A9D50113</GuidValue>\r
+    <Version>1.0</Version>\r
+    <Abstract>Component name for module Arp</Abstract>\r
+    <Description>FIX ME!</Description>\r
+    <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>\r
+    <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>\r
+    <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION   0x00000052</Specification>\r
+  </MsaHeader>\r
+  <ModuleDefinitions>\r
+    <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>\r
+    <BinaryModule>false</BinaryModule>\r
+    <OutputFileBasename>Arp</OutputFileBasename>\r
+  </ModuleDefinitions>\r
+  <LibraryClassDefinitions>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>DebugLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiDriverEntryPoint</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiBootServicesTableLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiLib</Keyword>\r
+    </LibraryClass>\r
+  </LibraryClassDefinitions>\r
+  <SourceFiles>\r
+    <Filename>ArpDriver.c</Filename>\r
+    <Filename>ArpDebug.h</Filename>\r
+    <Filename>ArpImpl.c</Filename>\r
+    <Filename>ArpImpl.h</Filename>\r
+    <Filename>ComponentName.c</Filename>\r
+    <Filename>ArpDriver.h</Filename>\r
+    <Filename>ArpMain.c</Filename>\r
+  </SourceFiles>\r
+  <PackageDependencies>\r
+    <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>\r
+    <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>\r
+  </PackageDependencies>\r
+  <Protocols>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiArpProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiManagedNetworkProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiArpServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiManagedNetworkServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+  </Protocols>\r
+  <Externs>\r
+    <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>\r
+    <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>\r
+    <Extern>\r
+      <ModuleEntryPoint>ArpDriverEntryPoint</ModuleEntryPoint>\r
+    </Extern>\r
+  </Externs>\r
+</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 (file)
index 0000000..93b4c10
--- /dev/null
@@ -0,0 +1,1628 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  ArpImpl.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+\r
+#include "ArpImpl.h"\r
+#include "ArpDebug.h"\r
+\r
+EFI_ARP_PROTOCOL  mEfiArpProtocolTemplate = {\r
+  ArpConfigure,\r
+  ArpAdd,\r
+  ArpFind,\r
+  ArpDelete,\r
+  ArpFlush,\r
+  ArpRequest,\r
+  ArpCancel\r
+};\r
+\r
+\r
+/**\r
+  Initialize the instance context data.\r
+\r
+  @param  ArpService             Pointer to the arp service context data this\r
+                                 instance belongs to.\r
+  @param  Instance               Pointer to the instance context data.\r
+\r
+  @return None.\r
+\r
+**/\r
+VOID\r
+ArpInitInstance (\r
+  IN ARP_SERVICE_DATA   *ArpService,\r
+  IN ARP_INSTANCE_DATA  *Instance\r
+  )\r
+{\r
+  NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);\r
+\r
+  Instance->Signature  = ARP_INSTANCE_DATA_SIGNATURE;\r
+  Instance->ArpService = ArpService;\r
+\r
+  CopyMem (&Instance->ArpProto, &mEfiArpProtocolTemplate, sizeof (ARP_SERVICE_DATA));\r
+\r
+  Instance->Configured = FALSE;\r
+  Instance->Destroyed  = FALSE;\r
+\r
+  NetListInit (&Instance->List);\r
+}\r
+\r
+\r
+/**\r
+  Process the Arp packets received from Mnp, the procedure conforms to RFC826.\r
+\r
+  @param  Event                  The Event this notify function registered to.\r
+  @param  Context                Pointer to the context data registerd to the\r
+                                 Event.\r
+\r
+  @return None.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ArpOnFrameRcvd (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  ARP_SERVICE_DATA                      *ArpService;\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *RxToken;\r
+  EFI_MANAGED_NETWORK_RECEIVE_DATA      *RxData;\r
+  ARP_HEAD                              *Head;\r
+  ARP_ADDRESS                           ArpAddress;\r
+  ARP_CACHE_ENTRY                       *CacheEntry;\r
+  NET_LIST_ENTRY                        *Entry;\r
+  ARP_INSTANCE_DATA                     *Instance;\r
+  EFI_ARP_CONFIG_DATA                   *ConfigData;\r
+  NET_ARP_ADDRESS                       SenderAddress[2];\r
+  BOOLEAN                               ProtoMatched;\r
+  BOOLEAN                               IsTarget;\r
+  BOOLEAN                               MergeFlag;\r
+\r
+  ArpService = (ARP_SERVICE_DATA *)Context;\r
+  NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);\r
+\r
+  RxToken = &ArpService->RxToken;\r
+\r
+  if (RxToken->Status == EFI_ABORTED) {\r
+    //\r
+    // The Token is aborted, possibly by arp itself, just return and the receiving\r
+    // process is stopped.\r
+    //\r
+    return;\r
+  }\r
+\r
+  if (EFI_ERROR (RxToken->Status)) {\r
+    //\r
+    // Restart the receiving if any other error Status occurs.\r
+    //\r
+    goto RESTART_RECEIVE;\r
+  }\r
+\r
+  //\r
+  // Status is EFI_SUCCESS, process the received frame.\r
+  //\r
+  RxData = RxToken->Packet.RxData;\r
+  Head   = (ARP_HEAD *) RxData->PacketData;\r
+\r
+  //\r
+  // Convert the byte order of the multi-byte fields.\r
+  //\r
+  Head->HwType    = NTOHS (Head->HwType);\r
+  Head->ProtoType = NTOHS (Head->ProtoType);\r
+  Head->OpCode    = NTOHS (Head->OpCode);\r
+\r
+  if ((Head->HwType != ArpService->SnpMode.IfType) ||\r
+    (Head->HwAddrLen != ArpService->SnpMode.HwAddressSize) ||\r
+    (RxData->ProtocolType != ARP_ETHER_PROTO_TYPE)) {\r
+    //\r
+    // The hardware type or the hardware address length doesn't match.\r
+    // There is a sanity check for the protocol type too.\r
+    //\r
+    goto RECYCLE_RXDATA;\r
+  }\r
+\r
+  //\r
+  // Set the pointers to the addresses contained in the arp packet.\r
+  //\r
+  ArpAddress.SenderHwAddr    = (UINT8 *)(Head + 1);\r
+  ArpAddress.SenderProtoAddr = ArpAddress.SenderHwAddr + Head->HwAddrLen;\r
+  ArpAddress.TargetHwAddr    = ArpAddress.SenderProtoAddr + Head->ProtoAddrLen;\r
+  ArpAddress.TargetProtoAddr = ArpAddress.TargetHwAddr + Head->HwAddrLen;\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    ARP_DEBUG_ERROR (("ArpOnFrameRcvd: Faild to acquire the CacheTableLock.\n"));\r
+    goto RECYCLE_RXDATA;\r
+  }\r
+\r
+  SenderAddress[Hardware].Type       = Head->HwType;\r
+  SenderAddress[Hardware].Length     = Head->HwAddrLen;\r
+  SenderAddress[Hardware].AddressPtr = ArpAddress.SenderHwAddr;\r
+\r
+  SenderAddress[Protocol].Type       = Head->ProtoType;\r
+  SenderAddress[Protocol].Length     = Head->ProtoAddrLen;\r
+  SenderAddress[Protocol].AddressPtr = ArpAddress.SenderProtoAddr;\r
+\r
+  //\r
+  // First, check the denied cache table.\r
+  //\r
+  CacheEntry = ArpFindDeniedCacheEntry (\r
+                 ArpService,\r
+                 &SenderAddress[Protocol],\r
+                 &SenderAddress[Hardware]\r
+                 );\r
+  if (CacheEntry != NULL) {\r
+    //\r
+    // This address (either hardware or protocol address, or both) is configured to\r
+    // be a deny entry, silently skip the normal process.\r
+    //\r
+    goto UNLOCK_EXIT;\r
+  }\r
+\r
+  ProtoMatched = FALSE;\r
+  IsTarget     = FALSE;\r
+  Instance     = NULL;\r
+  NET_LIST_FOR_EACH (Entry, &ArpService->ChildrenList) {\r
+    //\r
+    // Iterate all the children.\r
+    //\r
+    Instance = NET_LIST_USER_STRUCT (Entry, ARP_INSTANCE_DATA, List);\r
+    NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);\r
+    ConfigData = &Instance->ConfigData;\r
+\r
+    if ((Instance->Configured) &&\r
+      (Head->ProtoType == ConfigData->SwAddressType) &&\r
+      (Head->ProtoAddrLen == ConfigData->SwAddressLength)) {\r
+      //\r
+      // The protocol type is matched for the received arp packet.\r
+      //\r
+      ProtoMatched = TRUE;\r
+      if (0 == NetCompareMem (\r
+                 (VOID *)ArpAddress.TargetProtoAddr,\r
+                 ConfigData->StationAddress,\r
+                 ConfigData->SwAddressLength\r
+                 )) {\r
+        //\r
+        // The arp driver has the target address required by the received arp packet.\r
+        //\r
+        IsTarget = TRUE;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (!ProtoMatched) {\r
+    //\r
+    // Protocol type unmatchable, skip.\r
+    //\r
+    goto UNLOCK_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check whether the sender's address information is already in the cache.\r
+  //\r
+  MergeFlag  = FALSE;\r
+  CacheEntry = ArpFindNextCacheEntryInTable (\r
+                 &ArpService->ResolvedCacheTable,\r
+                 NULL,\r
+                 ByProtoAddress,\r
+                 &SenderAddress[Protocol],\r
+                 NULL\r
+                 );\r
+  if (CacheEntry != NULL) {\r
+    //\r
+    // Update the entry with the new information.\r
+    //\r
+    ArpFillAddressInCacheEntry (CacheEntry, &SenderAddress[Hardware], NULL);\r
+    CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;\r
+    MergeFlag = TRUE;\r
+  }\r
+\r
+  if (!IsTarget) {\r
+    //\r
+    // This arp packet isn't targeted to us, skip now.\r
+    //\r
+    goto UNLOCK_EXIT;\r
+  }\r
+\r
+  if (!MergeFlag) {\r
+    //\r
+    // Add the triplet <protocol type, sender protocol address, sender hardware address>\r
+    // to the translation table.\r
+    //\r
+    CacheEntry = ArpFindNextCacheEntryInTable (\r
+                   &ArpService->PendingRequestTable,\r
+                   NULL,\r
+                   ByProtoAddress,\r
+                   &SenderAddress[Protocol],\r
+                   NULL\r
+                   );\r
+    if (CacheEntry == NULL) {\r
+      //\r
+      // Allocate a new CacheEntry.\r
+      //\r
+      CacheEntry = ArpAllocCacheEntry (NULL);\r
+      if (CacheEntry == NULL) {\r
+        goto UNLOCK_EXIT;\r
+      }\r
+    }\r
+\r
+    NetListRemoveEntry (&CacheEntry->List);\r
+\r
+    //\r
+    // Fill the addresses into the CacheEntry.\r
+    //\r
+    ArpFillAddressInCacheEntry (\r
+      CacheEntry,\r
+      &SenderAddress[Hardware],\r
+      &SenderAddress[Protocol]\r
+      );\r
+\r
+    //\r
+    // Inform the user.\r
+    //\r
+    ArpAddressResolved (CacheEntry, NULL, NULL);\r
+\r
+    //\r
+    // Add this entry into the ResolvedCacheTable\r
+    //\r
+    NetListInsertHead (&ArpService->ResolvedCacheTable, &CacheEntry->List);\r
+  }\r
+\r
+  if (Head->OpCode == ARP_OPCODE_REQUEST) {\r
+    //\r
+    // Send back the ARP Reply. If we reach here, Instance is not NULL and CacheEntry\r
+    // is not NULL.\r
+    //\r
+    ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REPLY);\r
+  }\r
+\r
+UNLOCK_EXIT:\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+RECYCLE_RXDATA:\r
+\r
+  //\r
+  // Signal Mnp to recycle the RxData.\r
+  //\r
+  gBS->SignalEvent (RxData->RecycleEvent);\r
+\r
+RESTART_RECEIVE:\r
+\r
+  //\r
+  // Continue to receive packets from Mnp.\r
+  //\r
+  Status = ArpService->Mnp->Receive (ArpService->Mnp, RxToken);\r
+\r
+  DEBUG_CODE (\r
+    if (EFI_ERROR (Status)) {\r
+      ARP_DEBUG_ERROR (("ArpOnFrameRcvd: ArpService->Mnp->Receive "\r
+        "failed, %r\n.", Status));\r
+    }\r
+  );\r
+}\r
+\r
+\r
+/**\r
+  Process the already sent arp packets.\r
+\r
+  @param  Event                  The Event this notify function registered to.\r
+  @param  Context                Pointer to the context data registerd to the\r
+                                 Event.\r
+\r
+  @return None.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ArpOnFrameSent (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *TxToken;\r
+  EFI_MANAGED_NETWORK_TRANSMIT_DATA     *TxData;\r
+\r
+  ASSERT (Context != NULL);\r
+\r
+  TxToken = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *)Context;\r
+  TxData  = TxToken->Packet.TxData;\r
+\r
+  DEBUG_CODE (\r
+    if (EFI_ERROR (TxToken->Status)) {\r
+      ARP_DEBUG_ERROR (("ArpOnFrameSent: TxToken->Status, %r.\n", TxToken->Status));\r
+    }\r
+  );\r
+\r
+  //\r
+  // Free the allocated memory and close the event.\r
+  //\r
+  NetFreePool (TxData->FragmentTable[0].FragmentBuffer);\r
+  NetFreePool (TxData);\r
+  gBS->CloseEvent (TxToken->Event);\r
+  NetFreePool (TxToken);\r
+}\r
+\r
+\r
+/**\r
+  Process the arp cache olding and drive the retrying arp requests.\r
+\r
+  @param  Event                  The Event this notify function registered to.\r
+  @param  Context                Pointer to the context data registerd to the\r
+                                 Event.\r
+\r
+  @return None.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ArpTimerHandler (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  ARP_SERVICE_DATA      *ArpService;\r
+  NET_LIST_ENTRY        *Entry;\r
+  NET_LIST_ENTRY        *NextEntry;\r
+  NET_LIST_ENTRY        *ContextEntry;\r
+  ARP_CACHE_ENTRY       *CacheEntry;\r
+  USER_REQUEST_CONTEXT  *RequestContext;\r
+\r
+  ASSERT (Context != NULL);\r
+  ArpService = (ARP_SERVICE_DATA *)Context;\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Iterate all the pending requests to see whether a retry is needed to send out\r
+  // or the request finally fails because the retry time reaches the limitation.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) {\r
+    CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);\r
+\r
+    if (CacheEntry->NextRetryTime <= ARP_PERIODIC_TIMER_INTERVAL) {\r
+      //\r
+      // Timeout, if we can retry more, send out the request again, otherwise abort\r
+      // this request.\r
+      //\r
+      if (CacheEntry->RetryCount == 0) {\r
+        //\r
+        // Abort this request.\r
+        //\r
+        ArpAddressResolved (CacheEntry, NULL, NULL);\r
+        ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList));\r
+\r
+        NetListRemoveEntry (&CacheEntry->List);\r
+        NetFreePool (CacheEntry);\r
+      } else {\r
+        //\r
+        // resend the ARP request.\r
+        //\r
+        ASSERT (!NetListIsEmpty(&CacheEntry->UserRequestList));\r
+\r
+        ContextEntry   = CacheEntry->UserRequestList.ForwardLink;\r
+        RequestContext = NET_LIST_USER_STRUCT (ContextEntry, USER_REQUEST_CONTEXT, List);\r
+\r
+        ArpSendFrame (RequestContext->Instance, CacheEntry, ARP_OPCODE_REQUEST);\r
+\r
+        CacheEntry->RetryCount--;\r
+        CacheEntry->NextRetryTime = RequestContext->Instance->ConfigData.RetryTimeOut;\r
+      }\r
+    } else {\r
+      //\r
+      // Update the NextRetryTime.\r
+      //\r
+      CacheEntry->NextRetryTime -= ARP_PERIODIC_TIMER_INTERVAL;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check the timeouts for the DeniedCacheTable.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->DeniedCacheTable) {\r
+    CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);\r
+    ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList));\r
+\r
+    if (CacheEntry->DefaultDecayTime == 0) {\r
+      //\r
+      // It's a static entry, skip it.\r
+      //\r
+      continue;\r
+    }\r
+\r
+    if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) {\r
+      //\r
+      // Time out, remove it.\r
+      //\r
+      NetListRemoveEntry (&CacheEntry->List);\r
+      NetFreePool (CacheEntry);\r
+    } else {\r
+      //\r
+      // Update the DecayTime.\r
+      //\r
+      CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check the timeouts for the ResolvedCacheTable.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->ResolvedCacheTable) {\r
+    CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);\r
+    ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList));\r
+\r
+    if (CacheEntry->DefaultDecayTime == 0) {\r
+      //\r
+      // It's a static entry, skip it.\r
+      //\r
+      continue;\r
+    }\r
+\r
+    if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) {\r
+      //\r
+      // Time out, remove it.\r
+      //\r
+      NetListRemoveEntry (&CacheEntry->List);\r
+      NetFreePool (CacheEntry);\r
+    } else {\r
+      //\r
+      // Update the DecayTime.\r
+      //\r
+      CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL;\r
+    }\r
+  }\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+}\r
+\r
+\r
+/**\r
+  Match the two NET_ARP_ADDRESSes.\r
+\r
+  @param  AddressOne             Pointer to the first address to match.\r
+  @param  AddressTwo             Pointer to the second address to match.\r
+\r
+  @return The two addresses match or not.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+ArpMatchAddress (\r
+  IN NET_ARP_ADDRESS  *AddressOne,\r
+  IN NET_ARP_ADDRESS  *AddressTwo\r
+  )\r
+{\r
+  if ((AddressOne->Type != AddressTwo->Type) ||\r
+    (AddressOne->Length != AddressTwo->Length)) {\r
+    //\r
+    // Either Type or Length doesn't match.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  if ((AddressOne->AddressPtr != NULL) &&\r
+    (NetCompareMem (\r
+      AddressOne->AddressPtr,\r
+      AddressTwo->AddressPtr,\r
+      AddressOne->Length\r
+      ) != 0)) {\r
+    //\r
+    // The address is not the same.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Find the CacheEntry which matches the requirements in the specified CacheTable.\r
+\r
+  @param  CacheTable             Pointer to the arp cache table.\r
+  @param  StartEntry             Pointer to the start entry this search begins with\r
+                                 in the cache table.\r
+  @param  FindOpType             The search type.\r
+  @param  ProtocolAddress        Pointer to the protocol address to match.\r
+  @param  HardwareAddress        Pointer to the hardware address to match.\r
+\r
+  @return Pointer to the matched arp cache entry, if NULL, no match is found.\r
+\r
+**/\r
+ARP_CACHE_ENTRY *\r
+ArpFindNextCacheEntryInTable (\r
+  IN NET_LIST_ENTRY    *CacheTable,\r
+  IN NET_LIST_ENTRY    *StartEntry,\r
+  IN FIND_OPTYPE       FindOpType,\r
+  IN NET_ARP_ADDRESS   *ProtocolAddress OPTIONAL,\r
+  IN NET_ARP_ADDRESS   *HardwareAddress OPTIONAL\r
+  )\r
+{\r
+  NET_LIST_ENTRY   *Entry;\r
+  ARP_CACHE_ENTRY  *CacheEntry;\r
+\r
+  if (StartEntry == NULL) {\r
+    //\r
+    // Start from the beginning of the table if no StartEntry is specified.\r
+    //\r
+    StartEntry = CacheTable;\r
+  }\r
+\r
+  for (Entry = StartEntry->ForwardLink; Entry != CacheTable; Entry = Entry->ForwardLink) {\r
+    CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);\r
+\r
+    if (FindOpType & MATCH_SW_ADDRESS) {\r
+      //\r
+      // Find by the software address.\r
+      //\r
+      if (!ArpMatchAddress (ProtocolAddress, &CacheEntry->Addresses[Protocol])) {\r
+        //\r
+        // The ProtocolAddress doesn't match, continue to the next cache entry.\r
+        //\r
+        continue;\r
+      }\r
+    }\r
+\r
+    if (FindOpType & MATCH_HW_ADDRESS) {\r
+      //\r
+      // Find by the hardware address.\r
+      //\r
+      if (!ArpMatchAddress (HardwareAddress, &CacheEntry->Addresses[Hardware])) {\r
+        //\r
+        // The HardwareAddress doesn't match, continue to the next cache entry.\r
+        //\r
+        continue;\r
+      }\r
+    }\r
+\r
+    //\r
+    // The CacheEntry meets the requirements now, return this entry.\r
+    //\r
+    return CacheEntry;\r
+  }\r
+\r
+  //\r
+  // No matching.\r
+  //\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Find the CacheEntry, using ProtocolAddress or HardwareAddress or both, as the keyword,\r
+  in the DeniedCacheTable.\r
+\r
+  @param  ArpService             Pointer to the arp service context data.\r
+  @param  ProtocolAddress        Pointer to the protocol address.\r
+  @param  HardwareAddress        Pointer to the hardware address.\r
+\r
+  @return Pointer to the matched cache entry, if NULL no match is found.\r
+\r
+**/\r
+ARP_CACHE_ENTRY *\r
+ArpFindDeniedCacheEntry (\r
+  IN ARP_SERVICE_DATA  *ArpService,\r
+  IN NET_ARP_ADDRESS   *ProtocolAddress OPTIONAL,\r
+  IN NET_ARP_ADDRESS   *HardwareAddress OPTIONAL\r
+  )\r
+{\r
+  ARP_CACHE_ENTRY  *CacheEntry;\r
+\r
+  ASSERT ((ProtocolAddress != NULL) || (HardwareAddress != NULL));\r
+  NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE);\r
+\r
+  CacheEntry = NULL;\r
+\r
+  if ((ProtocolAddress != NULL) && (ProtocolAddress->AddressPtr != NULL)) {\r
+    //\r
+    // Find the cache entry in the DeniedCacheTable by the protocol address.\r
+    //\r
+    CacheEntry = ArpFindNextCacheEntryInTable (\r
+                   &ArpService->DeniedCacheTable,\r
+                   NULL,\r
+                   ByProtoAddress,\r
+                   ProtocolAddress,\r
+                   NULL\r
+                   );\r
+    if (CacheEntry != NULL) {\r
+      //\r
+      // There is a match.\r
+      //\r
+      return CacheEntry;\r
+    }\r
+  }\r
+\r
+  if ((HardwareAddress != NULL) && (HardwareAddress->AddressPtr != NULL)) {\r
+    //\r
+    // Find the cache entry in the DeniedCacheTable by the hardware address.\r
+    //\r
+    CacheEntry = ArpFindNextCacheEntryInTable (\r
+                   &ArpService->DeniedCacheTable,\r
+                   NULL,\r
+                   ByHwAddress,\r
+                   NULL,\r
+                   HardwareAddress\r
+                   );\r
+  }\r
+\r
+  return CacheEntry;\r
+}\r
+\r
+\r
+/**\r
+  Allocate a cache entry and initialize it.\r
+\r
+  @param  Instance               Pointer to the instance context data.\r
+\r
+  @return Pointer to the new created cache entry.\r
+\r
+**/\r
+ARP_CACHE_ENTRY *\r
+ArpAllocCacheEntry (\r
+  IN ARP_INSTANCE_DATA  *Instance\r
+  )\r
+{\r
+  ARP_CACHE_ENTRY  *CacheEntry;\r
+  NET_ARP_ADDRESS  *Address;\r
+  UINT16           Index;\r
+\r
+  //\r
+  // Allocate memory for the cache entry.\r
+  //\r
+  CacheEntry = NetAllocatePool (sizeof (ARP_CACHE_ENTRY));\r
+  if (CacheEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Init the lists.\r
+  //\r
+  NetListInit (&CacheEntry->List);\r
+  NetListInit (&CacheEntry->UserRequestList);\r
+\r
+  for (Index = 0; Index < 2; Index++) {\r
+    //\r
+    // Init the address pointers to point to the concrete buffer.\r
+    //\r
+    Address = &CacheEntry->Addresses[Index];\r
+    Address->AddressPtr = Address->Buffer.ProtoAddress;\r
+  }\r
+\r
+  //\r
+  // Zero the hardware address first.\r
+  //\r
+  NetZeroMem (CacheEntry->Addresses[Hardware].AddressPtr, ARP_MAX_HARDWARE_ADDRESS_LEN);\r
+\r
+  if (Instance != NULL) {\r
+    //\r
+    // Inherit the parameters from the instance configuration.\r
+    //\r
+    CacheEntry->RetryCount       = Instance->ConfigData.RetryCount;\r
+    CacheEntry->NextRetryTime    = Instance->ConfigData.RetryTimeOut;\r
+    CacheEntry->DefaultDecayTime = Instance->ConfigData.EntryTimeOut;\r
+    CacheEntry->DecayTime        = Instance->ConfigData.EntryTimeOut;\r
+  } else {\r
+    //\r
+    // Use the default parameters if this cache entry isn't allocate in a\r
+    // instance's  scope.\r
+    //\r
+    CacheEntry->RetryCount       = ARP_DEFAULT_RETRY_COUNT;\r
+    CacheEntry->NextRetryTime    = ARP_DEFAULT_RETRY_INTERVAL;\r
+    CacheEntry->DefaultDecayTime = ARP_DEFAULT_TIMEOUT_VALUE;\r
+    CacheEntry->DecayTime        = ARP_DEFAULT_TIMEOUT_VALUE;\r
+  }\r
+\r
+  return CacheEntry;\r
+}\r
+\r
+\r
+/**\r
+  Turn the CacheEntry into the resolved status.\r
+\r
+  @param  CacheEntry             Pointer to the resolved cache entry.\r
+  @param  Instance               Pointer to the instance context data.\r
+  @param  UserEvent              Pointer to the UserEvent to notify.\r
+\r
+  @return The count of notifications sent to the instance.\r
+\r
+**/\r
+UINTN\r
+ArpAddressResolved (\r
+  IN ARP_CACHE_ENTRY    *CacheEntry,\r
+  IN ARP_INSTANCE_DATA  *Instance OPTIONAL,\r
+  IN EFI_EVENT          UserEvent OPTIONAL\r
+  )\r
+{\r
+  NET_LIST_ENTRY        *Entry;\r
+  NET_LIST_ENTRY        *NextEntry;\r
+  USER_REQUEST_CONTEXT  *Context;\r
+  UINTN                 Count;\r
+\r
+  Count = 0;\r
+\r
+  //\r
+  // Iterate all the linked user requests to notify them.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &CacheEntry->UserRequestList) {\r
+    Context = NET_LIST_USER_STRUCT (Entry, USER_REQUEST_CONTEXT, List);\r
+\r
+    if (((Instance == NULL) || (Context->Instance == Instance)) &&\r
+      ((UserEvent == NULL) || (Context->UserRequestEvent == UserEvent))) {\r
+      //\r
+      // Copy the address to the user-provided buffer and notify the user.\r
+      //\r
+      NetCopyMem (\r
+        Context->UserHwAddrBuffer,\r
+        CacheEntry->Addresses[Hardware].AddressPtr,\r
+        CacheEntry->Addresses[Hardware].Length\r
+        );\r
+      gBS->SignalEvent (Context->UserRequestEvent);\r
+\r
+      //\r
+      // Remove this user request and free the context data.\r
+      //\r
+      NetListRemoveEntry (&Context->List);\r
+      NetFreePool (Context);\r
+\r
+      Count++;\r
+    }\r
+  }\r
+\r
+  return Count;\r
+}\r
+\r
+\r
+/**\r
+  Fill the addresses in the CacheEntry using the information passed in by\r
+  HwAddr and SwAddr.\r
+\r
+  @param  CacheEntry             Pointer to the cache entry.\r
+  @param  HwAddr                 Pointer to the software address.\r
+  @param  SwAddr                 Pointer to the hardware address.\r
+\r
+  @return None.\r
+\r
+**/\r
+VOID\r
+ArpFillAddressInCacheEntry (\r
+  IN ARP_CACHE_ENTRY  *CacheEntry,\r
+  IN NET_ARP_ADDRESS  *HwAddr OPTIONAL,\r
+  IN NET_ARP_ADDRESS  *SwAddr OPTIONAL\r
+  )\r
+{\r
+  NET_ARP_ADDRESS  *Address[2];\r
+  NET_ARP_ADDRESS  *CacheAddress;\r
+  UINT32           Index;\r
+\r
+  Address[Hardware] = HwAddr;\r
+  Address[Protocol] = SwAddr;\r
+\r
+  for (Index = 0; Index < 2; Index++) {\r
+    if (Address[Index] != NULL) {\r
+      //\r
+      // Fill the address if the passed in pointer is not NULL.\r
+      //\r
+      CacheAddress = &CacheEntry->Addresses[Index];\r
+\r
+      CacheAddress->Type   = Address[Index]->Type;\r
+      CacheAddress->Length = Address[Index]->Length;\r
+\r
+      if (Address[Index]->AddressPtr != NULL) {\r
+        //\r
+        // Copy it if the AddressPtr points to some buffer.\r
+        //\r
+        NetCopyMem (\r
+          CacheAddress->AddressPtr,\r
+          Address[Index]->AddressPtr,\r
+          CacheAddress->Length\r
+          );\r
+      } else {\r
+        //\r
+        // Zero the corresponding address buffer in the CacheEntry.\r
+        //\r
+        NetZeroMem (CacheAddress->AddressPtr, CacheAddress->Length);\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Configure the instance using the ConfigData. ConfigData is already validated.\r
+\r
+  @param  Instance               Pointer to the instance context data to be\r
+                                 configured.\r
+  @param  ConfigData             Pointer to the configuration data used to\r
+                                 configure the instance.\r
+\r
+  @retval EFI_SUCCESS            The instance is configured with the ConfigData.\r
+  @retval EFI_ACCESS_DENIED      The instance is already configured and the\r
+                                 ConfigData tries to reset some unchangeable\r
+                                 fields.\r
+  @retval EFI_INVALID_PARAMETER  The ConfigData provides a non-unicast IPv4 address\r
+                                 when the SwAddressType is IPv4.\r
+  @retval EFI_OUT_OF_RESOURCES   The instance fails to configure due to memory\r
+                                 limitation.\r
+\r
+**/\r
+EFI_STATUS\r
+ArpConfigureInstance (\r
+  IN ARP_INSTANCE_DATA    *Instance,\r
+  IN EFI_ARP_CONFIG_DATA  *ConfigData OPTIONAL\r
+  )\r
+{\r
+  EFI_ARP_CONFIG_DATA  *OldConfigData;\r
+  IP4_ADDR             Ip;\r
+\r
+  OldConfigData = &Instance->ConfigData;\r
+\r
+  if (ConfigData != NULL) {\r
+\r
+    if (Instance->Configured) {\r
+      //\r
+      // The instance is configured, check the unchangeable fields.\r
+      //\r
+      if ((OldConfigData->SwAddressType != ConfigData->SwAddressType) ||\r
+        (OldConfigData->SwAddressLength != ConfigData->SwAddressLength) ||\r
+        (NetCompareMem (\r
+           OldConfigData->StationAddress,\r
+           ConfigData->StationAddress,\r
+           OldConfigData->SwAddressLength\r
+           ) != 0)) {\r
+        //\r
+        // Deny the unallowed changes.\r
+        //\r
+        return EFI_ACCESS_DENIED;\r
+      }\r
+    } else {\r
+      //\r
+      // The instance is not configured.\r
+      //\r
+\r
+      if (ConfigData->SwAddressType == IPv4_ETHER_PROTO_TYPE) {\r
+        NetCopyMem (&Ip, ConfigData->StationAddress, sizeof (IP4_ADDR));\r
+\r
+        if (!Ip4IsUnicast (NTOHL (Ip), 0)) {\r
+          //\r
+          // The station address is not a valid IPv4 unicast address.\r
+          //\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+      }\r
+\r
+      //\r
+      // Save the configuration.\r
+      //\r
+      CopyMem (OldConfigData, ConfigData, sizeof (EFI_ARP_CONFIG_DATA));\r
+\r
+      OldConfigData->StationAddress = NetAllocatePool (OldConfigData->SwAddressLength);\r
+      if (OldConfigData->StationAddress == NULL) {\r
+        ARP_DEBUG_ERROR (("ArpConfigInstance: NetAllocatePool for the StationAddress "\r
+          "failed.\n"));\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      //\r
+      // Save the StationAddress.\r
+      //\r
+      NetCopyMem (\r
+        OldConfigData->StationAddress,\r
+        ConfigData->StationAddress,\r
+        OldConfigData->SwAddressLength\r
+        );\r
+\r
+      //\r
+      // Set the state to configured.\r
+      //\r
+      Instance->Configured = TRUE;\r
+    }\r
+\r
+    //\r
+    // Use the implementation specific values if the following field is zero.\r
+    //\r
+    OldConfigData->EntryTimeOut = (ConfigData->EntryTimeOut == 0) ?\r
+      ARP_DEFAULT_TIMEOUT_VALUE : ConfigData->EntryTimeOut;\r
+\r
+    OldConfigData->RetryCount   = (ConfigData->RetryCount == 0) ?\r
+      ARP_DEFAULT_RETRY_COUNT : ConfigData->RetryCount;\r
+\r
+    OldConfigData->RetryTimeOut = (ConfigData->RetryTimeOut == 0) ?\r
+      ARP_DEFAULT_RETRY_INTERVAL : ConfigData->RetryTimeOut;\r
+  } else {\r
+    //\r
+    // Reset the configuration.\r
+    //\r
+\r
+    if (Instance->Configured) {\r
+      //\r
+      // Cancel the arp requests issued by this instance.\r
+      //\r
+      ArpCancelRequest (Instance, NULL, NULL);\r
+\r
+      //\r
+      // Free the buffer previously allocated to hold the station address.\r
+      //\r
+      NetFreePool (OldConfigData->StationAddress);\r
+    }\r
+\r
+    Instance->Configured = FALSE;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Send out an arp frame using the CachEntry and the ArpOpCode.\r
+\r
+  @param  Instance               Pointer to the instance context data.\r
+  @param  CacheEntry             Pointer to the configuration data used to\r
+                                 configure the instance.\r
+  @param  ArpOpCode              The opcode used to send out this Arp frame, either\r
+                                 request or reply.\r
+\r
+  @return None.\r
+\r
+**/\r
+VOID\r
+ArpSendFrame (\r
+  IN ARP_INSTANCE_DATA  *Instance,\r
+  IN ARP_CACHE_ENTRY    *CacheEntry,\r
+  IN UINT16             ArpOpCode\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *TxToken;\r
+  EFI_MANAGED_NETWORK_TRANSMIT_DATA     *TxData;\r
+  UINT32                                TotalLength;\r
+  UINT8                                 *Packet;\r
+  ARP_SERVICE_DATA                      *ArpService;\r
+  EFI_SIMPLE_NETWORK_MODE               *SnpMode;\r
+  EFI_ARP_CONFIG_DATA                   *ConfigData;\r
+  UINT8                                 *TmpPtr;\r
+  ARP_HEAD                              *ArpHead;\r
+\r
+  ASSERT ((Instance != NULL) && (CacheEntry != NULL));\r
+\r
+  //\r
+  // Allocate memory for the TxToken.\r
+  //\r
+  TxToken = NetAllocatePool (sizeof(EFI_MANAGED_NETWORK_COMPLETION_TOKEN));\r
+  if (TxToken == NULL) {\r
+    ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for TxToken failed.\n"));\r
+    return;\r
+  }\r
+\r
+  TxToken->Event = NULL;\r
+  TxData         = NULL;\r
+  Packet         = NULL;\r
+\r
+  //\r
+  // Create the event for this TxToken.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  NET_TPL_EVENT,\r
+                  ArpOnFrameSent,\r
+                  (VOID *)TxToken,\r
+                  &TxToken->Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    ARP_DEBUG_ERROR (("ArpSendFrame: CreateEvent failed for TxToken->Event.\n"));\r
+    goto CLEAN_EXIT;\r
+  }\r
+\r
+  //\r
+  // Allocate memory for the TxData used in the TxToken.\r
+  //\r
+  TxData = NetAllocatePool (sizeof(EFI_MANAGED_NETWORK_TRANSMIT_DATA));\r
+  if (TxData == NULL) {\r
+    ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for TxData failed.\n"));\r
+    goto CLEAN_EXIT;\r
+  }\r
+\r
+  ArpService = Instance->ArpService;\r
+  SnpMode    = &ArpService->SnpMode;\r
+  ConfigData = &Instance->ConfigData;\r
+\r
+  //\r
+  // Calculate the buffer length for this arp frame.\r
+  //\r
+  TotalLength = SnpMode->MediaHeaderSize + sizeof (ARP_HEAD) +\r
+                2 * (ConfigData->SwAddressLength + SnpMode->HwAddressSize);\r
+\r
+  //\r
+  // Allocate buffer for the arp frame.\r
+  //\r
+  Packet = NetAllocatePool (TotalLength);\r
+  if (Packet == NULL) {\r
+    ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for Packet failed.\n"));\r
+  }\r
+\r
+  TmpPtr = Packet;\r
+\r
+  //\r
+  // The destination MAC address.\r
+  //\r
+  if (ArpOpCode == ARP_OPCODE_REQUEST) {\r
+    NetCopyMem (TmpPtr, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize);\r
+  } else {\r
+    NetCopyMem (\r
+      TmpPtr,\r
+      CacheEntry->Addresses[Hardware].AddressPtr,\r
+      SnpMode->HwAddressSize\r
+      );\r
+  }\r
+  TmpPtr += SnpMode->HwAddressSize;\r
+\r
+  //\r
+  // The source MAC address.\r
+  //\r
+  NetCopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize);\r
+  TmpPtr += SnpMode->HwAddressSize;\r
+\r
+  //\r
+  // The ethernet protocol type.\r
+  //\r
+  *(UINT16 *)TmpPtr = HTONS (ARP_ETHER_PROTO_TYPE);\r
+  TmpPtr            += 2;\r
+\r
+  //\r
+  // The ARP Head.\r
+  //\r
+  ArpHead               = (ARP_HEAD *) TmpPtr;\r
+  ArpHead->HwType       = HTONS ((UINT16)SnpMode->IfType);\r
+  ArpHead->ProtoType    = HTONS (ConfigData->SwAddressType);\r
+  ArpHead->HwAddrLen    = (UINT8)SnpMode->HwAddressSize;\r
+  ArpHead->ProtoAddrLen = ConfigData->SwAddressLength;\r
+  ArpHead->OpCode       = HTONS (ArpOpCode);\r
+  TmpPtr                += sizeof (ARP_HEAD);\r
+\r
+  //\r
+  // The sender hardware address.\r
+  //\r
+  NetCopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize);\r
+  TmpPtr += SnpMode->HwAddressSize;\r
+\r
+  //\r
+  // The sender protocol address.\r
+  //\r
+  NetCopyMem (TmpPtr, ConfigData->StationAddress, ConfigData->SwAddressLength);\r
+  TmpPtr += ConfigData->SwAddressLength;\r
+\r
+  //\r
+  // The target hardware address.\r
+  //\r
+  NetCopyMem (\r
+    TmpPtr,\r
+    CacheEntry->Addresses[Hardware].AddressPtr,\r
+    SnpMode->HwAddressSize\r
+    );\r
+  TmpPtr += SnpMode->HwAddressSize;\r
+\r
+  //\r
+  // The target protocol address.\r
+  //\r
+  NetCopyMem (\r
+    TmpPtr,\r
+    CacheEntry->Addresses[Protocol].AddressPtr,\r
+    ConfigData->SwAddressLength\r
+    );\r
+\r
+  //\r
+  // Set all the fields of the TxData.\r
+  //\r
+  TxData->DestinationAddress = NULL;\r
+  TxData->SourceAddress      = NULL;\r
+  TxData->ProtocolType       = 0;\r
+  TxData->DataLength         = TotalLength - SnpMode->MediaHeaderSize;\r
+  TxData->HeaderLength       = (UINT16) SnpMode->MediaHeaderSize;\r
+  TxData->FragmentCount      = 1;\r
+\r
+  TxData->FragmentTable[0].FragmentBuffer = Packet;\r
+  TxData->FragmentTable[0].FragmentLength = TotalLength;\r
+\r
+  //\r
+  // Associate the TxData with the TxToken.\r
+  //\r
+  TxToken->Packet.TxData = TxData;\r
+  TxToken->Status        = EFI_NOT_READY;\r
+\r
+  //\r
+  // Send out this arp packet by Mnp.\r
+  //\r
+  Status = ArpService->Mnp->Transmit (ArpService->Mnp, TxToken);\r
+  if (EFI_ERROR (Status)) {\r
+    ARP_DEBUG_ERROR (("Mnp->Transmit failed, %r.\n", Status));\r
+    goto CLEAN_EXIT;\r
+  }\r
+\r
+  return;\r
+\r
+CLEAN_EXIT:\r
+\r
+  if (Packet != NULL) {\r
+    NetFreePool (Packet);\r
+  }\r
+\r
+  if (TxData != NULL) {\r
+    NetFreePool (TxData);\r
+  }\r
+\r
+  if (TxToken->Event != NULL) {\r
+    gBS->CloseEvent (TxToken->Event);\r
+  }\r
+\r
+  NetFreePool (TxToken);\r
+}\r
+\r
+\r
+/**\r
+  Delete the cache entries in the specified CacheTable, using the BySwAddress,\r
+  SwAddressType, AddressBuffer combination as the matching key, if Force is TRUE,\r
+  the cache is deleted event it's a static entry.\r
+\r
+  @param  CacheTable             Pointer to the cache table to do the deletion.\r
+  @param  BySwAddress            Delete the cache entry by software address or by\r
+                                 hardware address.\r
+  @param  SwAddressType          The software address used to do the deletion.\r
+  @param  AddressBuffer          Pointer to the buffer containing the address to\r
+                                 match for the deletion.\r
+  @param  Force                  This deletion is forced or not.\r
+\r
+  @return The count of the deleted cache entries.\r
+\r
+**/\r
+STATIC\r
+UINTN\r
+ArpDeleteCacheEntryInTable (\r
+  IN NET_LIST_ENTRY  *CacheTable,\r
+  IN BOOLEAN         BySwAddress,\r
+  IN UINT16          SwAddressType,\r
+  IN UINT8           *AddressBuffer OPTIONAL,\r
+  IN BOOLEAN         Force\r
+  )\r
+{\r
+  NET_LIST_ENTRY   *Entry;\r
+  NET_LIST_ENTRY   *NextEntry;\r
+  ARP_CACHE_ENTRY  *CacheEntry;\r
+  UINTN            Count;\r
+\r
+  Count = 0;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, CacheTable) {\r
+    CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);\r
+\r
+    if ((CacheEntry->DefaultDecayTime == 0) && !Force) {\r
+      //\r
+      // It's a static entry and we are not forced to delete it, skip.\r
+      //\r
+      continue;\r
+    }\r
+\r
+    if (BySwAddress) {\r
+      if (SwAddressType == CacheEntry->Addresses[Protocol].Type) {\r
+        //\r
+        // Protocol address type matched. Check the address.\r
+        //\r
+        if ((AddressBuffer == NULL) ||\r
+          (NetCompareMem (\r
+             AddressBuffer,\r
+             CacheEntry->Addresses[Protocol].AddressPtr,\r
+             CacheEntry->Addresses[Protocol].Length\r
+             ) == 0)) {\r
+          //\r
+          // Address matched.\r
+          //\r
+          goto MATCHED;\r
+        }\r
+      }\r
+    } else {\r
+      if ((AddressBuffer == NULL) ||\r
+        (NetCompareMem (\r
+           AddressBuffer,\r
+           CacheEntry->Addresses[Hardware].AddressPtr,\r
+           CacheEntry->Addresses[Hardware].Length\r
+           ) == 0)) {\r
+        //\r
+        // Address matched.\r
+        //\r
+        goto MATCHED;\r
+      }\r
+    }\r
+\r
+    continue;\r
+\r
+MATCHED:\r
+\r
+    //\r
+    // Delete this entry.\r
+    //\r
+    NetListRemoveEntry (&CacheEntry->List);\r
+    ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList));\r
+    NetFreePool (CacheEntry);\r
+\r
+    Count++;\r
+  }\r
+\r
+  return Count;\r
+}\r
+\r
+\r
+/**\r
+  Delete cache entries in all the cache tables.\r
+\r
+  @param  Instance               Pointer to the instance context data.\r
+  @param  BySwAddress            Delete the cache entry by software address or by\r
+                                 hardware address.\r
+  @param  AddressBuffer          Pointer to the buffer containing the address to\r
+                                 match for the deletion.\r
+  @param  Force                  This deletion is forced or not.\r
+\r
+  @return The count of the deleted cache entries.\r
+\r
+**/\r
+UINTN\r
+ArpDeleteCacheEntry (\r
+  IN ARP_INSTANCE_DATA  *Instance,\r
+  IN BOOLEAN            BySwAddress,\r
+  IN UINT8              *AddressBuffer OPTIONAL,\r
+  IN BOOLEAN            Force\r
+  )\r
+{\r
+  ARP_SERVICE_DATA  *ArpService;\r
+  UINTN             Count;\r
+\r
+  NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);\r
+\r
+  ArpService = Instance->ArpService;\r
+\r
+  //\r
+  // Delete the cache entries in the DeniedCacheTable.\r
+  //\r
+  Count = ArpDeleteCacheEntryInTable (\r
+            &ArpService->DeniedCacheTable,\r
+            BySwAddress,\r
+            Instance->ConfigData.SwAddressType,\r
+            AddressBuffer,\r
+            Force\r
+            );\r
+\r
+  //\r
+  // Delete the cache entries inthe ResolvedCacheTable.\r
+  //\r
+  Count += ArpDeleteCacheEntryInTable (\r
+             &ArpService->ResolvedCacheTable,\r
+             BySwAddress,\r
+             Instance->ConfigData.SwAddressType,\r
+             AddressBuffer,\r
+             Force\r
+             );\r
+\r
+  return Count;\r
+}\r
+\r
+\r
+/**\r
+  Cancel the arp request.\r
+\r
+  @param  Instance               Pointer to the instance context data.\r
+  @param  TargetSwAddress        Pointer to the buffer containing the target\r
+                                 software address to match the arp request.\r
+  @param  UserEvent              The user event used to notify this request\r
+                                 cancellation.\r
+\r
+  @return The count of the cancelled requests.\r
+\r
+**/\r
+UINTN\r
+ArpCancelRequest (\r
+  IN ARP_INSTANCE_DATA  *Instance,\r
+  IN VOID               *TargetSwAddress OPTIONAL,\r
+  IN EFI_EVENT          UserEvent        OPTIONAL\r
+  )\r
+{\r
+  ARP_SERVICE_DATA  *ArpService;\r
+  NET_LIST_ENTRY    *Entry;\r
+  NET_LIST_ENTRY    *NextEntry;\r
+  ARP_CACHE_ENTRY   *CacheEntry;\r
+  UINTN             Count;\r
+\r
+  NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE);\r
+\r
+  ArpService = Instance->ArpService;\r
+\r
+  Count = 0;\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) {\r
+    CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List);\r
+\r
+    if ((TargetSwAddress == NULL) ||\r
+      (NetCompareMem (\r
+         TargetSwAddress,\r
+         CacheEntry->Addresses[Protocol].AddressPtr,\r
+         CacheEntry->Addresses[Protocol].Length\r
+         ) == 0)) {\r
+      //\r
+      // This request entry matches the TargetSwAddress or all requests are to be\r
+      // cancelled as TargetSwAddress is NULL.\r
+      //\r
+      Count += ArpAddressResolved (CacheEntry, Instance, UserEvent);\r
+\r
+      if (NetListIsEmpty (&CacheEntry->UserRequestList)) {\r
+        //\r
+        // No user requests any more, remove this request cache entry.\r
+        //\r
+        NetListRemoveEntry (&CacheEntry->List);\r
+        NetFreePool (CacheEntry);\r
+      }\r
+    }\r
+  }\r
+\r
+  return Count;\r
+}\r
+\r
+\r
+/**\r
+  Find the cache entry in the cache table.\r
+\r
+  @param  Instance               Pointer to the instance context data.\r
+  @param  BySwAddress            Set to TRUE to look for matching software protocol\r
+                                 addresses. Set to FALSE to look for matching\r
+                                 hardware protocol addresses.\r
+  @param  AddressBuffer          Pointer to address buffer. Set to NULL to match\r
+                                 all addresses.\r
+  @param  EntryLength            The size of an entry in the entries buffer.\r
+  @param  EntryCount             The number of ARP cache entries that are found by\r
+                                 the specified criteria.\r
+  @param  Entries                Pointer to the buffer that will receive the ARP\r
+                                 cache entries.\r
+  @param  Refresh                Set to TRUE to refresh the timeout value of the\r
+                                 matching ARP cache entry.\r
+\r
+  @retval EFI_SUCCESS            The requested ARP cache entries are copied into\r
+                                 the buffer.\r
+  @retval EFI_NOT_FOUND          No matching entries found.\r
+  @retval EFI_OUT_OF_RESOURCE    There is a memory allocation failure.\r
+\r
+**/\r
+EFI_STATUS\r
+ArpFindCacheEntry (\r
+  IN ARP_INSTANCE_DATA   *Instance,\r
+  IN BOOLEAN             BySwAddress,\r
+  IN VOID                *AddressBuffer OPTIONAL,\r
+  OUT UINT32             *EntryLength   OPTIONAL,\r
+  OUT UINT32             *EntryCount    OPTIONAL,\r
+  OUT EFI_ARP_FIND_DATA  **Entries      OPTIONAL,\r
+  IN BOOLEAN             Refresh\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  ARP_SERVICE_DATA   *ArpService;\r
+  NET_ARP_ADDRESS    MatchAddress;\r
+  FIND_OPTYPE        FindOpType;\r
+  NET_LIST_ENTRY     *StartEntry;\r
+  ARP_CACHE_ENTRY    *CacheEntry;\r
+  NET_MAP            FoundEntries;\r
+  UINT32             FoundCount;\r
+  EFI_ARP_FIND_DATA  *FindData;\r
+  NET_LIST_ENTRY     *CacheTable;\r
+\r
+  ArpService = Instance->ArpService;\r
+\r
+  //\r
+  // Init the FounEntries used to hold the found cache entries.\r
+  //\r
+  NetMapInit (&FoundEntries);\r
+\r
+  //\r
+  // Set the MatchAddress.\r
+  //\r
+  if (BySwAddress) {\r
+    MatchAddress.Type   = Instance->ConfigData.SwAddressType;\r
+    MatchAddress.Length = Instance->ConfigData.SwAddressLength;\r
+    FindOpType          = ByProtoAddress;\r
+  } else {\r
+    MatchAddress.Type   = ArpService->SnpMode.IfType;\r
+    MatchAddress.Length = (UINT8)ArpService->SnpMode.HwAddressSize;\r
+    FindOpType          = ByHwAddress;\r
+  }\r
+\r
+  MatchAddress.AddressPtr = AddressBuffer;\r
+\r
+  //\r
+  // Search the DeniedCacheTable\r
+  //\r
+  StartEntry = NULL;\r
+  while (TRUE) {\r
+    //\r
+    // Try to find the matched entries in the DeniedCacheTable.\r
+    //\r
+    CacheEntry = ArpFindNextCacheEntryInTable (\r
+                   &ArpService->DeniedCacheTable,\r
+                   StartEntry,\r
+                   FindOpType,\r
+                   &MatchAddress,\r
+                   &MatchAddress\r
+                   );\r
+    if (CacheEntry == NULL) {\r
+      //\r
+      // Once the CacheEntry is NULL, there are no more matches.\r
+      //\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Insert the found entry into the map.\r
+    //\r
+    NetMapInsertTail (\r
+      &FoundEntries,\r
+      (VOID *)CacheEntry,\r
+      (VOID *)&ArpService->DeniedCacheTable\r
+      );\r
+\r
+    //\r
+    // Let the next search start from this cache entry.\r
+    //\r
+    StartEntry = &CacheEntry->List;\r
+\r
+    if (Refresh) {\r
+      //\r
+      // Refresh the DecayTime if needed.\r
+      //\r
+      CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Search the ResolvedCacheTable\r
+  //\r
+  StartEntry = NULL;\r
+  while (TRUE) {\r
+    CacheEntry = ArpFindNextCacheEntryInTable (\r
+                   &ArpService->ResolvedCacheTable,\r
+                   StartEntry,\r
+                   FindOpType,\r
+                   &MatchAddress,\r
+                   &MatchAddress\r
+                   );\r
+    if (CacheEntry == NULL) {\r
+      //\r
+      // Once the CacheEntry is NULL, there are no more matches.\r
+      //\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Insert the found entry into the map.\r
+    //\r
+    NetMapInsertTail (\r
+      &FoundEntries,\r
+      (VOID *)CacheEntry,\r
+      (VOID *)&ArpService->ResolvedCacheTable\r
+      );\r
+\r
+    //\r
+    // Let the next search start from this cache entry.\r
+    //\r
+    StartEntry = &CacheEntry->List;\r
+\r
+    if (Refresh) {\r
+      //\r
+      // Refresh the DecayTime if needed.\r
+      //\r
+      CacheEntry->DecayTime = CacheEntry->DefaultDecayTime;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  FoundCount = (UINT32) NetMapGetCount (&FoundEntries);\r
+  if (FoundCount == 0) {\r
+    Status = EFI_NOT_FOUND;\r
+    goto CLEAN_EXIT;\r
+  }\r
+\r
+  if (EntryLength != NULL) {\r
+    //\r
+    // Return the entry length.\r
+    //\r
+    *EntryLength = sizeof (EFI_ARP_FIND_DATA) + Instance->ConfigData.SwAddressLength +\r
+                   ArpService->SnpMode.HwAddressSize;\r
+  }\r
+\r
+  if (EntryCount != NULL) {\r
+    //\r
+    // Return the found entry count.\r
+    //\r
+    *EntryCount = FoundCount;\r
+  }\r
+\r
+  if (Entries == NULL) {\r
+    goto CLEAN_EXIT;\r
+  }\r
+\r
+  //\r
+  // Allocate buffer to copy the found entries.\r
+  //\r
+  FindData = NetAllocatePool (FoundCount * (*EntryLength));\r
+  if (FindData == NULL) {\r
+    ARP_DEBUG_ERROR (("ArpFindCacheEntry: Failed to allocate memory.\n"));\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto CLEAN_EXIT;\r
+  }\r
+\r
+  //\r
+  // Return the address to the user.\r
+  //\r
+  *Entries = FindData;\r
+\r
+  //\r
+  // Dump the entries.\r
+  //\r
+  while (!NetMapIsEmpty (&FoundEntries)) {\r
+    //\r
+    // Get a cache entry from the map.\r
+    //\r
+    CacheEntry = NetMapRemoveHead (&FoundEntries, (VOID **)&CacheTable);\r
+\r
+    //\r
+    // Set the fields in FindData.\r
+    //\r
+    FindData->Size            = *EntryLength;\r
+    FindData->DenyFlag        = (BOOLEAN)(CacheTable == &ArpService->DeniedCacheTable);\r
+    FindData->StaticFlag      = (BOOLEAN)(CacheEntry->DefaultDecayTime == 0);\r
+    FindData->HwAddressType   = ArpService->SnpMode.IfType;\r
+    FindData->SwAddressType   = Instance->ConfigData.SwAddressType;\r
+    FindData->HwAddressLength = (UINT8)ArpService->SnpMode.HwAddressSize;\r
+    FindData->SwAddressLength = Instance->ConfigData.SwAddressLength;\r
+\r
+    //\r
+    // Copy the software address.\r
+    //\r
+    NetCopyMem (\r
+      FindData + 1,\r
+      CacheEntry->Addresses[Protocol].AddressPtr,\r
+      FindData->SwAddressLength\r
+      );\r
+\r
+    //\r
+    // Copy the hardware address.\r
+    //\r
+    NetCopyMem (\r
+      (UINT8 *)(FindData + 1) + FindData->SwAddressLength,\r
+      CacheEntry->Addresses[Hardware].AddressPtr,\r
+      FindData->HwAddressLength\r
+      );\r
+\r
+    //\r
+    // Slip to the next FindData.\r
+    //\r
+    FindData = (EFI_ARP_FIND_DATA *)((UINT8 *)FindData + *EntryLength);\r
+  }\r
+\r
+CLEAN_EXIT:\r
+\r
+  NetMapClean (&FoundEntries);\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h
new file mode 100644 (file)
index 0000000..607443a
--- /dev/null
@@ -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_
+
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/Arp.h>\r
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/ServiceBinding.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#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 (file)
index 0000000..eb1b082
--- /dev/null
@@ -0,0 +1,727 @@
+/** @file\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  ArpMain.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "ArpImpl.h"\r
+\r
+\r
+/**\r
+  This function is used to assign a station address to the ARP cache for this instance\r
+  of the ARP driver. A call to this function with the ConfigData field set to NULL\r
+  will reset this ARP instance.\r
+\r
+  @param  This                   Pointer to the EFI_ARP_PROTOCOL instance.\r
+  @param  ConfigData             Pointer to the EFI_ARP_CONFIG_DATA structure.\r
+\r
+  @retval EFI_SUCCESS            The new station address was successfully\r
+                                 registered.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL. SwAddressLength is zero when\r
+                                 ConfigData is not NULL. StationAddress is NULL\r
+                                 when ConfigData is not NULL.\r
+  @retval EFI_ACCESS_DENIED      The SwAddressType, SwAddressLength, or\r
+                                 StationAddress is different from the one that is\r
+                                 already registered.\r
+  @retval EFI_OUT_OF_RESOURCES   Storage for the new StationAddress could not be\r
+                                 allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpConfigure (\r
+  IN EFI_ARP_PROTOCOL     *This,\r
+  IN EFI_ARP_CONFIG_DATA  *ConfigData OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  ARP_INSTANCE_DATA  *Instance;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((ConfigData != NULL) &&\r
+    ((ConfigData->SwAddressLength == 0) ||\r
+    (ConfigData->StationAddress == NULL) ||\r
+    (ConfigData->SwAddressType <= 1500))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = ARP_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&Instance->ArpService->Lock))) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Configure this instance, the ConfigData has already passed the basic checks.\r
+  //\r
+  Status = ArpConfigureInstance (Instance, ConfigData);\r
+\r
+  NET_UNLOCK (&Instance->ArpService->Lock);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is used to insert entries into the ARP cache.\r
+\r
+  @param  This                   Pointer to the EFI_ARP_PROTOCOL instance.\r
+  @param  DenyFlag               Set to TRUE if this entry is a deny entry. Set to\r
+                                 FALSE if this  entry is a normal entry.\r
+  @param  TargetSwAddress        Pointer to a protocol address to add (or deny).\r
+                                 May be set to NULL if DenyFlag is TRUE.\r
+  @param  TargetHwAddress        Pointer to a hardware address to add (or deny).\r
+                                 May be set to NULL if DenyFlag is TRUE.\r
+  @param  TimeoutValue           Time in 100-ns units that this entry will remain\r
+                                 in the ARP cache. A value of zero means that the\r
+                                 entry is permanent. A nonzero value will override\r
+                                 the one given by Configure() if the entry to be\r
+                                 added is a dynamic entry.\r
+  @param  Overwrite              If TRUE, the matching cache entry will be\r
+                                 overwritten with the supplied parameters. If\r
+                                 FALSE, EFI_ACCESS_DENIED is returned if the\r
+                                 corresponding cache entry already exists.\r
+\r
+  @retval EFI_SUCCESS            The entry has been added or updated.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL. DenyFlag is FALSE and\r
+                                 TargetHwAddress is NULL. DenyFlag is FALSE and\r
+                                 TargetSwAddress is NULL. TargetHwAddress is NULL\r
+                                 and TargetSwAddress is NULL. Both TargetSwAddress\r
+                                 and TargetHwAddress are not NULL when DenyFlag is\r
+                                 TRUE.\r
+  @retval EFI_OUT_OF_RESOURCES   The new ARP cache entry could not be allocated.\r
+  @retval EFI_ACCESS_DENIED      The ARP cache entry already exists and Overwrite\r
+                                 is not true.\r
+  @retval EFI_NOT_STARTED        The ARP driver instance has not been configured.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpAdd (\r
+  IN EFI_ARP_PROTOCOL  *This,\r
+  IN BOOLEAN           DenyFlag,\r
+  IN VOID              *TargetSwAddress OPTIONAL,\r
+  IN VOID              *TargetHwAddress OPTIONAL,\r
+  IN UINT32            TimeoutValue,\r
+  IN BOOLEAN           Overwrite\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  ARP_INSTANCE_DATA        *Instance;\r
+  ARP_SERVICE_DATA         *ArpService;\r
+  ARP_CACHE_ENTRY          *CacheEntry;\r
+  EFI_SIMPLE_NETWORK_MODE  *SnpMode;\r
+  NET_ARP_ADDRESS          MatchAddress[2];\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (((!DenyFlag) && ((TargetHwAddress == NULL) || (TargetSwAddress == NULL))) ||\r
+    (DenyFlag && (TargetHwAddress != NULL) && (TargetSwAddress != NULL)) ||\r
+    ((TargetHwAddress == NULL) && (TargetSwAddress == NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = ARP_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  Status     = EFI_SUCCESS;\r
+  ArpService = Instance->ArpService;\r
+  SnpMode    = &Instance->ArpService->SnpMode;\r
+\r
+  //\r
+  // Fill the hardware address part in the MatchAddress.\r
+  //\r
+  MatchAddress[Hardware].Type       = SnpMode->IfType;\r
+  MatchAddress[Hardware].Length     = (UINT8) SnpMode->HwAddressSize;\r
+  MatchAddress[Hardware].AddressPtr = TargetHwAddress;\r
+\r
+  //\r
+  // Fill the software address part in the MatchAddress.\r
+  //\r
+  MatchAddress[Protocol].Type       = Instance->ConfigData.SwAddressType;\r
+  MatchAddress[Protocol].Length     = Instance->ConfigData.SwAddressLength;\r
+  MatchAddress[Protocol].AddressPtr = TargetSwAddress;\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // See whether the entry to add exists. Check the DeinedCacheTable first.\r
+  //\r
+  CacheEntry = ArpFindDeniedCacheEntry (\r
+                 ArpService,\r
+                 &MatchAddress[Protocol],\r
+                 &MatchAddress[Hardware]\r
+                 );\r
+\r
+  if (CacheEntry == NULL) {\r
+    //\r
+    // Check the ResolvedCacheTable\r
+    //\r
+    CacheEntry = ArpFindNextCacheEntryInTable (\r
+                   &ArpService->ResolvedCacheTable,\r
+                   NULL,\r
+                   ByBoth,\r
+                   &MatchAddress[Protocol],\r
+                   &MatchAddress[Hardware]\r
+                   );\r
+  }\r
+\r
+  if ((CacheEntry != NULL) && !Overwrite) {\r
+    //\r
+    // The entry to add exists, if not Overwirte, deny this add request.\r
+    //\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto UNLOCK_EXIT;\r
+  }\r
+\r
+  if ((CacheEntry == NULL) && (TargetSwAddress != NULL)) {\r
+    //\r
+    // Check whether there are pending requests matching the entry to be added.\r
+    //\r
+    CacheEntry = ArpFindNextCacheEntryInTable (\r
+                   &ArpService->PendingRequestTable,\r
+                   NULL,\r
+                   ByProtoAddress,\r
+                   &MatchAddress[Protocol],\r
+                   NULL\r
+                   );\r
+  }\r
+\r
+  if (CacheEntry != NULL) {\r
+    //\r
+    // Remove it from the Table.\r
+    //\r
+    NetListRemoveEntry (&CacheEntry->List);\r
+  } else {\r
+    //\r
+    // It's a new entry, allocate memory for the entry.\r
+    //\r
+    CacheEntry = ArpAllocCacheEntry (Instance);\r
+\r
+    if (CacheEntry == NULL) {\r
+      ARP_DEBUG_ERROR (("ArpAdd: Failed to allocate pool for CacheEntry.\n"));\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto UNLOCK_EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Overwrite these parameters.\r
+  //\r
+  CacheEntry->DefaultDecayTime = TimeoutValue;\r
+  CacheEntry->DecayTime        = TimeoutValue;\r
+\r
+  //\r
+  // Fill in the addresses.\r
+  //\r
+  ArpFillAddressInCacheEntry (\r
+    CacheEntry,\r
+    &MatchAddress[Hardware],\r
+    &MatchAddress[Protocol]\r
+    );\r
+\r
+  //\r
+  // Inform the user if there is any.\r
+  //\r
+  ArpAddressResolved (CacheEntry, NULL, NULL);\r
+\r
+  //\r
+  // Add this CacheEntry to the corresponding CacheTable.\r
+  //\r
+  if (DenyFlag) {\r
+    NetListInsertHead (&ArpService->DeniedCacheTable, &CacheEntry->List);\r
+  } else {\r
+    NetListInsertHead (&ArpService->ResolvedCacheTable, &CacheEntry->List);\r
+  }\r
+\r
+UNLOCK_EXIT:\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function searches the ARP cache for matching entries and allocates a buffer into\r
+  which those entries are copied.\r
+\r
+  @param  This                   Pointer to the EFI_ARP_PROTOCOL instance.\r
+  @param  BySwAddress            Set to TRUE to look for matching software protocol\r
+                                 addresses. Set to FALSE to look for matching\r
+                                 hardware protocol addresses.\r
+  @param  AddressBuffer          Pointer to address buffer. Set to NULL to match\r
+                                 all addresses.\r
+  @param  EntryLength            The size of an entry in the entries buffer.\r
+  @param  EntryCount             The number of ARP cache entries that are found by\r
+                                 the specified criteria.\r
+  @param  Entries                Pointer to the buffer that will receive the ARP\r
+                                 cache entries.\r
+  @param  Refresh                Set to TRUE to refresh the timeout value of the\r
+                                 matching ARP cache entry.\r
+\r
+  @retval EFI_SUCCESS            The requested ARP cache entries were copied into\r
+                                 the buffer.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL. Both EntryCount and EntryLength are\r
+                                 NULL, when Refresh is FALSE.\r
+  @retval EFI_NOT_FOUND          No matching entries were found.\r
+  @retval EFI_NOT_STARTED        The ARP driver instance has not been configured.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpFind (\r
+  IN EFI_ARP_PROTOCOL    *This,\r
+  IN BOOLEAN             BySwAddress,\r
+  IN VOID                *AddressBuffer OPTIONAL,\r
+  OUT UINT32             *EntryLength   OPTIONAL,\r
+  OUT UINT32             *EntryCount    OPTIONAL,\r
+  OUT EFI_ARP_FIND_DATA  **Entries      OPTIONAL,\r
+  IN BOOLEAN             Refresh\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  ARP_INSTANCE_DATA  *Instance;\r
+  ARP_SERVICE_DATA   *ArpService;\r
+\r
+  if ((This == NULL) ||\r
+    (!Refresh && (EntryCount == NULL) && (EntryLength == NULL)) ||\r
+    ((Entries != NULL) && ((EntryLength == NULL) || (EntryCount == NULL)))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance   = ARP_INSTANCE_DATA_FROM_THIS (This);\r
+  ArpService = Instance->ArpService;\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // All the check passed, find the cache entries now.\r
+  //\r
+  Status = ArpFindCacheEntry (\r
+             Instance,\r
+             BySwAddress,\r
+             AddressBuffer,\r
+             EntryLength,\r
+             EntryCount,\r
+             Entries,\r
+             Refresh\r
+             );\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function removes specified ARP cache entries.\r
+\r
+  @param  This                   Pointer to the EFI_ARP_PROTOCOL instance.\r
+  @param  BySwAddress            Set to TRUE to delete matching protocol addresses.\r
+                                 Set to FALSE to delete matching hardware\r
+                                 addresses.\r
+  @param  AddressBuffer          Pointer to the address buffer that is used as a\r
+                                 key to look for the cache entry. Set to NULL to\r
+                                 delete all entries.\r
+\r
+  @retval EFI_SUCCESS            The entry was removed from the ARP cache.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_NOT_FOUND          The specified deletion key was not found.\r
+  @retval EFI_NOT_STARTED        The ARP driver instance has not been configured.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpDelete (\r
+  IN EFI_ARP_PROTOCOL  *This,\r
+  IN BOOLEAN           BySwAddress,\r
+  IN VOID              *AddressBuffer OPTIONAL\r
+  )\r
+{\r
+  ARP_INSTANCE_DATA  *Instance;\r
+  ARP_SERVICE_DATA   *ArpService;\r
+  UINTN              Count;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = ARP_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  ArpService = Instance->ArpService;\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Delete the specified cache entries.\r
+  //\r
+  Count = ArpDeleteCacheEntry (Instance, BySwAddress, AddressBuffer, TRUE);\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+  return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This function delete all dynamic entries from the ARP cache that match the specified\r
+  software protocol type.\r
+\r
+  @param  This                   Pointer to the EFI_ARP_PROTOCOL instance.\r
+\r
+  @retval EFI_SUCCESS            The cache has been flushed.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_NOT_FOUND          There are no matching dynamic cache entries.\r
+  @retval EFI_NOT_STARTED        The ARP driver instance has not been configured.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpFlush (\r
+  IN EFI_ARP_PROTOCOL  *This\r
+  )\r
+{\r
+  ARP_INSTANCE_DATA  *Instance;\r
+  ARP_SERVICE_DATA   *ArpService;\r
+  UINTN              Count;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = ARP_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  ArpService = Instance->ArpService;\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Delete the dynamic entries from the cache table.\r
+  //\r
+  Count = ArpDeleteCacheEntry (Instance, FALSE, NULL, FALSE);\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+  return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This function tries to resolve the TargetSwAddress and optionally returns a\r
+  TargetHwAddress if it already exists in the ARP cache.\r
+\r
+  @param  This                   Pointer to the EFI_ARP_PROTOCOL instance.\r
+  @param  TargetSwAddress        Pointer to the protocol address to resolve.\r
+  @param  ResolvedEvent          Pointer to the event that will be signaled when\r
+                                 the address is resolved or some error occurs.\r
+  @param  TargetHwAddress        Pointer to the buffer for the resolved hardware\r
+                                 address in network byte order.\r
+\r
+  @retval EFI_SUCCESS            The data is copied from the ARP cache into the\r
+                                 TargetHwAddress buffer.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL. TargetHwAddress is NULL.\r
+  @retval EFI_ACCESS_DENIED      The requested address is not present in the normal\r
+                                 ARP cache but is present in the deny address list.\r
+                                 Outgoing traffic to that address is forbidden.\r
+  @retval EFI_NOT_STARTED        The ARP driver instance has not been configured.\r
+  @retval EFI_NOT_READY          The request has been started and is not finished.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpRequest (\r
+  IN EFI_ARP_PROTOCOL  *This,\r
+  IN VOID              *TargetSwAddress OPTIONAL,\r
+  IN EFI_EVENT         ResolvedEvent    OPTIONAL,\r
+  OUT VOID             *TargetHwAddress\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  ARP_INSTANCE_DATA        *Instance;\r
+  ARP_SERVICE_DATA         *ArpService;\r
+  EFI_SIMPLE_NETWORK_MODE  *SnpMode;\r
+  ARP_CACHE_ENTRY          *CacheEntry;\r
+  NET_ARP_ADDRESS          HardwareAddress;\r
+  NET_ARP_ADDRESS          ProtocolAddress;\r
+  USER_REQUEST_CONTEXT     *RequestContext;\r
+\r
+  if ((This == NULL) || (TargetHwAddress == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = ARP_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  Status     = EFI_SUCCESS;\r
+  ArpService = Instance->ArpService;\r
+  SnpMode    = &ArpService->SnpMode;\r
+\r
+  if ((TargetSwAddress == NULL) ||\r
+    ((Instance->ConfigData.SwAddressType == IPv4_ETHER_PROTO_TYPE) &&\r
+    IP4_IS_LOCAL_BROADCAST (*((UINT32 *)TargetSwAddress)))) {\r
+    //\r
+    // Return the hardware broadcast address.\r
+    //\r
+    NetCopyMem (TargetHwAddress, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize);\r
+\r
+    goto SIGNAL_USER;\r
+  }\r
+\r
+  if ((Instance->ConfigData.SwAddressType == IPv4_ETHER_PROTO_TYPE) &&\r
+    IP4_IS_MULTICAST (NTOHL (*((UINT32 *)TargetSwAddress)))) {\r
+    //\r
+    // If the software address is an IPv4 multicast address, invoke Mnp to\r
+    // resolve the address.\r
+    //\r
+    Status = ArpService->Mnp->McastIpToMac (\r
+                                ArpService->Mnp,\r
+                                FALSE,\r
+                                TargetSwAddress,\r
+                                TargetHwAddress\r
+                                );\r
+    goto SIGNAL_USER;\r
+  }\r
+\r
+  HardwareAddress.Type       = SnpMode->IfType;\r
+  HardwareAddress.Length     = (UINT8)SnpMode->HwAddressSize;\r
+  HardwareAddress.AddressPtr = NULL;\r
+\r
+  ProtocolAddress.Type       = Instance->ConfigData.SwAddressType;\r
+  ProtocolAddress.Length     = Instance->ConfigData.SwAddressLength;\r
+  ProtocolAddress.AddressPtr = TargetSwAddress;\r
+\r
+  //\r
+  // Initialize the TargetHwAddrss to a zero address.\r
+  //\r
+  NetZeroMem (TargetHwAddress, SnpMode->HwAddressSize);\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Check whether the software address is in the denied table.\r
+  //\r
+  CacheEntry = ArpFindDeniedCacheEntry (ArpService, &ProtocolAddress, NULL);\r
+  if (CacheEntry != NULL) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto UNLOCK_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check whether the software address is already resolved.\r
+  //\r
+  CacheEntry = ArpFindNextCacheEntryInTable (\r
+                 &ArpService->ResolvedCacheTable,\r
+                 NULL,\r
+                 ByProtoAddress,\r
+                 &ProtocolAddress,\r
+                 NULL\r
+                 );\r
+  if (CacheEntry != NULL) {\r
+    //\r
+    // Resolved, copy the address into the user buffer.\r
+    //\r
+    NetCopyMem (\r
+      TargetHwAddress,\r
+      CacheEntry->Addresses[Hardware].AddressPtr,\r
+      CacheEntry->Addresses[Hardware].Length\r
+      );\r
+\r
+    goto UNLOCK_EXIT;\r
+  }\r
+\r
+  if (ResolvedEvent == NULL) {\r
+    Status = EFI_NOT_READY;\r
+    goto UNLOCK_EXIT;\r
+  }\r
+\r
+  //\r
+  // Create a request context for this arp request.\r
+  //\r
+  RequestContext = NetAllocatePool (sizeof(USER_REQUEST_CONTEXT));\r
+  if (RequestContext == NULL) {\r
+    ARP_DEBUG_ERROR (("ArpRequest: Allocate memory for RequestContext failed.\n"));\r
+\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto UNLOCK_EXIT;\r
+  }\r
+\r
+  RequestContext->Instance         = Instance;\r
+  RequestContext->UserRequestEvent = ResolvedEvent;\r
+  RequestContext->UserHwAddrBuffer = TargetHwAddress;\r
+  NetListInit (&RequestContext->List);\r
+\r
+  //\r
+  // Check whether there is a same request.\r
+  //\r
+  CacheEntry = ArpFindNextCacheEntryInTable (\r
+                 &ArpService->PendingRequestTable,\r
+                 NULL,\r
+                 ByProtoAddress,\r
+                 &ProtocolAddress,\r
+                 NULL\r
+                 );\r
+  if (CacheEntry != NULL) {\r
+\r
+    CacheEntry->NextRetryTime = Instance->ConfigData.RetryTimeOut;\r
+    CacheEntry->RetryCount    = Instance->ConfigData.RetryCount;\r
+  } else {\r
+    //\r
+    // Allocate a cache entry for this request.\r
+    //\r
+    CacheEntry = ArpAllocCacheEntry (Instance);\r
+    if (CacheEntry == NULL) {\r
+      ARP_DEBUG_ERROR (("ArpRequest: Allocate memory for CacheEntry failed.\n"));\r
+      NetFreePool (RequestContext);\r
+\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto UNLOCK_EXIT;\r
+    }\r
+\r
+    //\r
+    // Fill the software address.\r
+    //\r
+    ArpFillAddressInCacheEntry (CacheEntry, &HardwareAddress, &ProtocolAddress);\r
+\r
+    //\r
+    // Add this entry into the PendingRequestTable.\r
+    //\r
+    NetListInsertTail (&ArpService->PendingRequestTable, &CacheEntry->List);\r
+  }\r
+\r
+  //\r
+  // Link this request context into the cache entry.\r
+  //\r
+  NetListInsertHead (&CacheEntry->UserRequestList, &RequestContext->List);\r
+\r
+  //\r
+  // Send out the ARP Request frame.\r
+  //\r
+  ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REQUEST);\r
+  Status = EFI_NOT_READY;\r
+\r
+UNLOCK_EXIT:\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+SIGNAL_USER:\r
+\r
+  if ((ResolvedEvent != NULL) && (Status == EFI_SUCCESS)) {\r
+    gBS->SignalEvent (ResolvedEvent);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function aborts the previous ARP request (identified by This,  TargetSwAddress\r
+  and ResolvedEvent) that is issued by EFI_ARP_PROTOCOL.Request().\r
+\r
+  @param  This                   Pointer to the EFI_ARP_PROTOCOL instance.\r
+  @param  TargetSwAddress        Pointer to the protocol address in previous\r
+                                 request session.\r
+  @param  ResolvedEvent          Pointer to the event that is used as the\r
+                                 notification event in previous request session.\r
+\r
+  @retval EFI_SUCCESS            The pending request session(s) is/are aborted and\r
+                                 corresponding event(s) is/are signaled.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL. TargetSwAddress is not NULL and\r
+                                 ResolvedEvent is NULL. TargetSwAddress is NULL and\r
+                                 ResolvedEvent is not NULL.\r
+  @retval EFI_NOT_STARTED        The ARP driver instance has not been configured.\r
+  @retval EFI_NOT_FOUND          The request is not issued by\r
+                                 EFI_ARP_PROTOCOL.Request().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArpCancel (\r
+  IN EFI_ARP_PROTOCOL  *This,\r
+  IN VOID              *TargetSwAddress OPTIONAL,\r
+  IN EFI_EVENT         ResolvedEvent    OPTIONAL\r
+  )\r
+{\r
+  ARP_INSTANCE_DATA  *Instance;\r
+  ARP_SERVICE_DATA   *ArpService;\r
+  UINTN              Count;\r
+\r
+  if ((This == NULL) ||\r
+    ((TargetSwAddress != NULL) && (ResolvedEvent == NULL)) ||\r
+    ((TargetSwAddress == NULL) && (ResolvedEvent != NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = ARP_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  ArpService = Instance->ArpService;\r
+\r
+  if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Cancel the specified request.\r
+  //\r
+  Count = ArpCancelRequest (Instance, TargetSwAddress, ResolvedEvent);\r
+\r
+  NET_UNLOCK (&ArpService->Lock);\r
+\r
+  return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c b/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c
new file mode 100644 (file)
index 0000000..ad92f3c
--- /dev/null
@@ -0,0 +1,156 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  ComponentName.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "ArpDriver.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+ArpComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+ArpComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+EFI_COMPONENT_NAME_PROTOCOL  gArpComponentName = {\r
+  ArpComponentNameGetDriverName,\r
+  ArpComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+STATIC EFI_UNICODE_STRING_TABLE mArpDriverNameTable[] = {\r
+  { "eng", L"ARP Network Service Driver" },\r
+  { NULL, NULL }\r
+};\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+ArpComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the EFI Driver.\r
+\r
+  Arguments:\r
+    This       - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    Language   - A pointer to a three character ISO 639-2 language identifier.\r
+                 This is the language of the driver name that that the caller\r
+                 is requesting, and it must match one of the languages specified\r
+                 in SupportedLanguages.  The number of languages supported by a\r
+                 driver is up to the driver writer.\r
+    DriverName - A pointer to the Unicode string to return.  This Unicode string\r
+                 is the name of the driver specified by This in the language\r
+                 specified by Language.\r
+\r
+  Returns:\r
+    EFI_SUCCES            - The Unicode string for the Driver specified by This\r
+                            and the language specified by Language was returned\r
+                            in DriverName.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - DriverName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return LookupUnicodeString (\r
+           Language,\r
+           gArpComponentName.SupportedLanguages,\r
+           mArpDriverNameTable,\r
+           DriverName\r
+           );\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+ArpComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the controller\r
+    that is being managed by an EFI Driver.\r
+\r
+  Arguments:\r
+    This             - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    ControllerHandle - The handle of a controller that the driver specified by\r
+                       This is managing.  This handle specifies the controller\r
+                       whose name is to be returned.\r
+    ChildHandle      - The handle of the child controller to retrieve the name\r
+                       of.  This is an optional parameter that may be NULL.  It\r
+                       will be NULL for device drivers.  It will also be NULL\r
+                       for a bus drivers that wish to retrieve the name of the\r
+                       bus controller.  It will not be NULL for a bus driver\r
+                       that wishes to retrieve the name of a child controller.\r
+    Language         - A pointer to a three character ISO 639-2 language\r
+                       identifier.  This is the language of the controller name\r
+                       that that the caller is requesting, and it must match one\r
+                       of the languages specified in SupportedLanguages.  The\r
+                       number of languages supported by a driver is up to the\r
+                       driver writer.\r
+    ControllerName   - A pointer to the Unicode string to return.  This Unicode\r
+                       string is the name of the controller specified by\r
+                       ControllerHandle and ChildHandle in the language specified\r
+                       by Language from the point of view of the driver specified\r
+                       by This.\r
+\r
+  Returns:\r
+    EFI_SUCCESS           - The Unicode string for the user readable name in the\r
+                            language specified by Language for the driver\r
+                            specified by This was returned in DriverName.\r
+    EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - ControllerName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This is not currently managing\r
+                            the controller specified by ControllerHandle and\r
+                            ChildHandle.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c
new file mode 100644 (file)
index 0000000..6808e77
--- /dev/null
@@ -0,0 +1,162 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  ComponentName.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+\r
+#include "Dhcp4Impl.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+DhcpComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+DhcpComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+EFI_COMPONENT_NAME_PROTOCOL     gDhcp4ComponentName = {\r
+  DhcpComponentNameGetDriverName,\r
+  DhcpComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+static EFI_UNICODE_STRING_TABLE mDhcpDriverNameTable[] = {\r
+  {\r
+    "eng",\r
+    L"DHCP Protocol Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+DhcpComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the EFI Driver.\r
+\r
+  Arguments:\r
+    This       - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    Language   - A pointer to a three character ISO 639-2 language identifier.\r
+                 This is the language of the driver name that that the caller\r
+                 is requesting, and it must match one of the languages specified\r
+                 in SupportedLanguages.  The number of languages supported by a\r
+                 driver is up to the driver writer.\r
+    DriverName - A pointer to the Unicode string to return.  This Unicode string\r
+                 is the name of the driver specified by This in the language\r
+                 specified by Language.\r
+\r
+  Returns:\r
+    EFI_SUCCES            - The Unicode string for the Driver specified by This\r
+                            and the language specified by Language was returned\r
+                            in DriverName.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - DriverName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return LookupUnicodeString (\r
+          Language,\r
+          gDhcp4ComponentName.SupportedLanguages,\r
+          mDhcpDriverNameTable,\r
+          DriverName\r
+          );\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+DhcpComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the controller\r
+    that is being managed by an EFI Driver.\r
+\r
+  Arguments:\r
+    This             - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    ControllerHandle - The handle of a controller that the driver specified by\r
+                       This is managing.  This handle specifies the controller\r
+                       whose name is to be returned.\r
+    ChildHandle      - The handle of the child controller to retrieve the name\r
+                       of.  This is an optional parameter that may be NULL.  It\r
+                       will be NULL for device drivers.  It will also be NULL\r
+                       for a bus drivers that wish to retrieve the name of the\r
+                       bus controller.  It will not be NULL for a bus driver\r
+                       that wishes to retrieve the name of a child controller.\r
+    Language         - A pointer to a three character ISO 639-2 language\r
+                       identifier.  This is the language of the controller name\r
+                       that that the caller is requesting, and it must match one\r
+                       of the languages specified in SupportedLanguages.  The\r
+                       number of languages supported by a driver is up to the\r
+                       driver writer.\r
+    ControllerName   - A pointer to the Unicode string to return.  This Unicode\r
+                       string is the name of the controller specified by\r
+                       ControllerHandle and ChildHandle in the language\r
+                       specified by Language from the point of view of the\r
+                       driver specified by This.\r
+\r
+  Returns:\r
+    EFI_SUCCESS           - The Unicode string for the user readable name in the\r
+                            language specified by Language for the driver\r
+                            specified by This was returned in DriverName.\r
+    EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - ChildHandle isn't NULL and isn't a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - ControllerName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This is not currently\r
+                            managing the controller specified by\r
+                            ControllerHandle and ChildHandle.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c
new file mode 100644 (file)
index 0000000..08d6076
--- /dev/null
@@ -0,0 +1,665 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+Module Name:\r
+\r
+  Dhcp4Driver.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Dhcp4Impl.h"\r
+#include "Dhcp4Driver.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gDhcp4DriverBinding = {\r
+  Dhcp4DriverBindingSupported,\r
+  Dhcp4DriverBindingStart,\r
+  Dhcp4DriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL mDhcp4ServiceBindingTemplete = {\r
+  Dhcp4ServiceBindingCreateChild,\r
+  Dhcp4ServiceBindingDestroyChild\r
+};\r
+\r
+//@MT: EFI_DRIVER_ENTRY_POINT (Dhcp4DriverEntryPoint)\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp4DriverEntryPoint (\r
+  IN EFI_HANDLE             ImageHandle,\r
+  IN EFI_SYSTEM_TABLE       *SystemTable\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Entry point of the DHCP driver to install various protocols.\r
+\r
+Arguments:\r
+\r
+  ImageHandle - The driver's image handle\r
+  SystemTable - The system table\r
+\r
+Returns:\r
+\r
+  EFI_SUCCESS - All the related protocols are installed.\r
+  Others      - Failed to install the protocols.\r
+\r
+--*/\r
+{\r
+  return NetLibInstallAllDriverProtocols (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gDhcp4DriverBinding,\r
+           ImageHandle,\r
+           &gDhcp4ComponentName,\r
+           NULL,\r
+           NULL\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Test to see if DHCP driver supports the ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to test\r
+  @param  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                 device to start.\r
+\r
+  @retval EFI_SUCCES             This driver supports this device\r
+  @retval other                  This driver does not support this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp4DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiUdp4ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+\r
+/**\r
+  Configure the default UDP child to receive all the DHCP traffics\r
+  on this network interface.\r
+\r
+  @param  UdpIo                  The UDP IO port to configure\r
+  @param  Context                The context to the function\r
+\r
+  @retval EFI_SUCCESS            The UDP IO port is successfully configured.\r
+  @retval Others                 Failed to configure the UDP child.\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpConfigUdpIo (\r
+  IN UDP_IO_PORT            *UdpIo,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_UDP4_CONFIG_DATA      UdpConfigData;\r
+\r
+  UdpConfigData.AcceptBroadcast           = TRUE;\r
+  UdpConfigData.AcceptPromiscuous         = FALSE;\r
+  UdpConfigData.AcceptAnyPort             = FALSE;\r
+  UdpConfigData.AllowDuplicatePort        = TRUE;\r
+  UdpConfigData.TypeOfService             = 0;\r
+  UdpConfigData.TimeToLive                = 64;\r
+  UdpConfigData.DoNotFragment             = FALSE;\r
+  UdpConfigData.ReceiveTimeout            = 0;\r
+  UdpConfigData.TransmitTimeout           = 0;\r
+\r
+  UdpConfigData.UseDefaultAddress         = FALSE;\r
+  UdpConfigData.StationPort               = DHCP_CLIENT_PORT;\r
+  UdpConfigData.RemotePort                = DHCP_SERVER_PORT;\r
+\r
+  NetZeroMem (&UdpConfigData.StationAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  NetZeroMem (&UdpConfigData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+  NetZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  return UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);;\r
+}\r
+\r
+\r
+\r
+/**\r
+  Destory the DHCP service. The Dhcp4 service may be partly initialized,\r
+  or partly destoried. If a resource is destoried, it is marked as so in\r
+  case the destory failed and being called again later.\r
+\r
+  @param  DhcpSb                 The DHCP service instance to destory.\r
+\r
+  @retval EFI_SUCCESS            The DHCP service is successfully closed.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp4CloseService (\r
+  IN DHCP_SERVICE           *DhcpSb\r
+  )\r
+{\r
+  DhcpCleanLease (DhcpSb);\r
+\r
+  if (DhcpSb->UdpIo != NULL) {\r
+    UdpIoFreePort (DhcpSb->UdpIo);\r
+    DhcpSb->UdpIo = NULL;\r
+  }\r
+\r
+  if (DhcpSb->Timer != NULL) {\r
+    gBS->SetTimer (DhcpSb->Timer, TimerCancel, 0);\r
+    gBS->CloseEvent (DhcpSb->Timer);\r
+\r
+    DhcpSb->Timer = NULL;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+/**\r
+  Create a new DHCP service binding instance for the controller.\r
+\r
+  @param  Controller             The controller to install DHCP service binding\r
+                                 protocol onto\r
+  @param  ImageHandle            The driver's image handle\r
+  @param  Service                The variable to receive the created DHCP service\r
+                                 instance.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource .\r
+  @retval EFI_SUCCESS            The DHCP service instance is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp4CreateService (\r
+  IN  EFI_HANDLE            Controller,\r
+  IN  EFI_HANDLE            ImageHandle,\r
+  OUT DHCP_SERVICE          **Service\r
+  )\r
+{\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_STATUS                Status;\r
+\r
+  *Service  = NULL;\r
+  DhcpSb    = NetAllocateZeroPool (sizeof (DHCP_SERVICE));\r
+\r
+  if (DhcpSb == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  DhcpSb->Signature       = DHCP_SERVICE_SIGNATURE;\r
+  DhcpSb->ServiceBinding  = mDhcp4ServiceBindingTemplete;\r
+  DhcpSb->ServiceState    = DHCP_UNCONFIGED;\r
+  DhcpSb->InDestory       = FALSE;\r
+  DhcpSb->Controller      = Controller;\r
+  DhcpSb->Image           = ImageHandle;\r
+  NetListInit (&DhcpSb->Children);\r
+  DhcpSb->DhcpState       = Dhcp4Stopped;\r
+  DhcpSb->Xid             = NET_RANDOM (NetRandomInitSeed ());\r
+\r
+  //\r
+  // Create various resources, UdpIo, Timer, and get Mac address\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  DhcpOnTimerTick,\r
+                  DhcpSb,\r
+                  &DhcpSb->Timer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  DhcpSb->UdpIo = UdpIoCreatePort (Controller, ImageHandle, DhcpConfigUdpIo, NULL);\r
+\r
+  if (DhcpSb->UdpIo == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  DhcpSb->HwLen  = (UINT8) DhcpSb->UdpIo->SnpMode.HwAddressSize;\r
+  DhcpSb->HwType = DhcpSb->UdpIo->SnpMode.IfType;\r
+  CopyMem (&DhcpSb->Mac, &DhcpSb->UdpIo->SnpMode.CurrentAddress, sizeof (EFI_MAC_ADDRESS));\r
+\r
+  *Service       = DhcpSb;\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  Dhcp4CloseService (DhcpSb);\r
+  NetFreePool (DhcpSb);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to bind driver to\r
+  @param  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                 device to start.\r
+\r
+  @retval EFI_SUCCES             This driver is added to ControllerHandle\r
+  @retval EFI_ALREADY_STARTED    This driver is already running on ControllerHandle\r
+  @retval other                  This driver does not support this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp4DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // First: test for the DHCP4 Protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiDhcp4ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Install the Dhcp4ServiceBinding Protocol onto ControlerHandle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &ControllerHandle,\r
+                  &gEfiDhcp4ServiceBindingProtocolGuid,\r
+                  &DhcpSb->ServiceBinding,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return Status;\r
+\r
+ON_ERROR:\r
+  Dhcp4CloseService (DhcpSb);\r
+  NetFreePool (DhcpSb);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to stop driver on\r
+  @param  NumberOfChildren       Number of Handles in ChildHandleBuffer. If number\r
+                                 of  children is zero stop the entire bus driver.\r
+  @param  ChildHandleBuffer      List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCES             This driver is removed ControllerHandle\r
+  @retval other                  This driver was not removed from this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp4DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  DHCP_SERVICE                  *DhcpSb;\r
+  DHCP_PROTOCOL                 *Instance;\r
+  EFI_HANDLE                    NicHandle;\r
+  EFI_STATUS                    Status;\r
+  EFI_TPL                       OldTpl;\r
+\r
+  //\r
+  // DHCP driver opens UDP child, So, the ControllerHandle is the\r
+  // UDP child handle. locate the Nic handle first.\r
+  //\r
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);\r
+\r
+  if (NicHandle == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+   Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  &gEfiDhcp4ServiceBindingProtocolGuid,\r
+                  (VOID **) &ServiceBinding,\r
+                  This->DriverBindingHandle,\r
+                  NicHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding);\r
+\r
+  if (DhcpSb->InDestory) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  OldTpl            = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  DhcpSb->InDestory = TRUE;\r
+\r
+  //\r
+  // Don't use NET_LIST_FOR_EACH_SAFE here, Dhcp4ServiceBindingDestoryChild\r
+  // may cause other child to be deleted.\r
+  //\r
+  while (!NetListIsEmpty (&DhcpSb->Children)) {\r
+    Instance = NET_LIST_HEAD (&DhcpSb->Children, DHCP_PROTOCOL, Link);\r
+    Dhcp4ServiceBindingDestroyChild (ServiceBinding, Instance->Handle);\r
+  }\r
+\r
+  if (DhcpSb->NumChildren != 0) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  DhcpSb->ServiceState  = DHCP_DESTORY;\r
+\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  NicHandle,\r
+                  &gEfiDhcp4ServiceBindingProtocolGuid,\r
+                  ServiceBinding\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Dhcp4CloseService (DhcpSb);\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  NetFreePool (DhcpSb);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  DhcpSb->InDestory = FALSE;\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Initialize a new DHCP child\r
+\r
+  @param  DhcpSb                 The dhcp service instance\r
+  @param  Instance               The dhcp instance to initialize\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+DhcpInitProtocol (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN DHCP_PROTOCOL          *Instance\r
+  )\r
+{\r
+  Instance->Signature         = DHCP_PROTOCOL_SIGNATURE;\r
+  CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (EFI_DHCP4_PROTOCOL));\r
+  NetListInit (&Instance->Link);\r
+  Instance->Handle            = NULL;\r
+  Instance->Service           = DhcpSb;\r
+  Instance->InDestory         = FALSE;\r
+  Instance->CompletionEvent   = NULL;\r
+  Instance->RenewRebindEvent  = NULL;\r
+  Instance->Token             = NULL;\r
+}\r
+\r
+\r
+/**\r
+  Creates a child handle with a set of DHCP4 services.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ChildHandle            Pointer to the handle of the child to create.  If\r
+                                 it  is NULL, then a new handle is created.  If it\r
+                                 is not  NULL, then the DHCP4 services are added to\r
+                                 the existing  child handle.\r
+\r
+  @retval EFI_SUCCES             The child handle was created with the DHCP4\r
+                                 services\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources to create the child\r
+  @retval other                  The child handle was not created\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp4ServiceBindingCreateChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    *ChildHandle\r
+  )\r
+{\r
+  DHCP_SERVICE              *DhcpSb;\r
+  DHCP_PROTOCOL             *Instance;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  VOID                      *Udp4;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = NetAllocatePool (sizeof (*Instance));\r
+\r
+  if (Instance == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  DhcpSb = DHCP_SERVICE_FROM_THIS (This);\r
+  DhcpInitProtocol (DhcpSb, Instance);\r
+\r
+  //\r
+  // Install DHCP4 onto ChildHandle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiDhcp4ProtocolGuid,\r
+                  &Instance->Dhcp4Protocol,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (Instance);\r
+    return Status;\r
+  }\r
+\r
+  Instance->Handle  = *ChildHandle;\r
+\r
+  //\r
+  // Open the Udp4 protocol BY_CHILD.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  DhcpSb->UdpIo->UdpHandle,\r
+                  &gEfiUdp4ProtocolGuid,\r
+                  (VOID **) &Udp4,\r
+                  gDhcp4DriverBinding.DriverBindingHandle,\r
+                  Instance->Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Instance->Handle,\r
+           &gEfiDhcp4ProtocolGuid,\r
+           &Instance->Dhcp4Protocol,\r
+           NULL\r
+           );\r
+\r
+    NetFreePool (Instance);\r
+    return Status;\r
+  }\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  NetListInsertTail (&DhcpSb->Children, &Instance->Link);\r
+  DhcpSb->NumChildren++;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Destroys a child handle with a set of DHCP4 services.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ChildHandle            Handle of the child to destroy\r
+\r
+  @retval EFI_SUCCES             The DHCP4 service is removed from the child handle\r
+  @retval EFI_UNSUPPORTED        The child handle does not support the DHCP4\r
+                                 service\r
+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.\r
+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because\r
+                                 its  DHCP4 services are being used.\r
+  @retval other                  The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp4ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  )\r
+{\r
+  DHCP_SERVICE              *DhcpSb;\r
+  DHCP_PROTOCOL             *Instance;\r
+  EFI_DHCP4_PROTOCOL        *Dhcp;\r
+  EFI_TPL                   OldTpl;\r
+  EFI_STATUS                Status;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Retrieve the private context data structures\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiDhcp4ProtocolGuid,\r
+                  (VOID **) &Dhcp,\r
+                  gDhcp4DriverBinding.DriverBindingHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Instance  = DHCP_INSTANCE_FROM_THIS (Dhcp);\r
+  DhcpSb    = DHCP_SERVICE_FROM_THIS (This);\r
+\r
+  if (Instance->Service != DhcpSb) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // A child can be destoried more than once. For example,\r
+  // Dhcp4DriverBindingStop will destory all of its children.\r
+  // when caller driver is being stopped, it will destory the\r
+  // dhcp child it opens.\r
+  //\r
+  if (Instance->InDestory) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  Instance->InDestory = TRUE;\r
+\r
+  //\r
+  // Close the Udp4 protocol.\r
+  //\r
+  gBS->CloseProtocol (\r
+         DhcpSb->UdpIo->UdpHandle,\r
+         &gEfiUdp4ProtocolGuid,\r
+         gDhcp4DriverBinding.DriverBindingHandle,\r
+         ChildHandle\r
+         );\r
+\r
+  //\r
+  // Uninstall the DHCP4 protocol first to enable a top down destruction.\r
+  //\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  ChildHandle,\r
+                  &gEfiDhcp4ProtocolGuid,\r
+                  Dhcp\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Instance->InDestory = FALSE;\r
+\r
+    NET_RESTORE_TPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  if (DhcpSb->ActiveChild == Instance) {\r
+    DhcpYieldControl (DhcpSb);\r
+  }\r
+\r
+  NetListRemoveEntry (&Instance->Link);\r
+  DhcpSb->NumChildren--;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  NetFreePool (Instance);\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h
new file mode 100644 (file)
index 0000000..100bf56
--- /dev/null
@@ -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 (file)
index 0000000..2c35be1
--- /dev/null
@@ -0,0 +1,65 @@
+#/** @file\r
+# Component name for module Dhcp4\r
+#\r
+# Copyright (c) 2007, Intel Corporation\r
+#\r
+#  All rights reserved. This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution. The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = Dhcp4Dxe\r
+  FILE_GUID                      = 94734718-0BBC-47fb-96A5-EE7A5AE6A2AD\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  EDK_RELEASE_VERSION            = 0x00020000\r
+  EFI_SPECIFICATION_VERSION      = 0x00020000\r
+\r
+  ENTRY_POINT                    = Dhcp4DriverEntryPoint\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources.common]\r
+  Dhcp4Impl.c\r
+  Dhcp4Io.c\r
+  Dhcp4Io.h\r
+  ComponentName.c\r
+  Dhcp4Driver.h\r
+  Dhcp4Driver.c\r
+  Dhcp4Option.c\r
+  Dhcp4Option.h\r
+  Dhcp4Impl.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  UefiLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  DebugLib\r
+  NetLib\r
+  UdpIoLib\r
+\r
+\r
+[Protocols]\r
+  gEfiUdp4ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiDhcp4ServiceBindingProtocolGuid           # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiUdp4ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiDhcp4ProtocolGuid                         # PROTOCOL ALWAYS_CONSUMED\r
+\r
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa
new file mode 100644 (file)
index 0000000..6bde71a
--- /dev/null
@@ -0,0 +1,77 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+  <MsaHeader>\r
+    <ModuleName>Dhcp4Dxe</ModuleName>\r
+    <ModuleType>DXE_DRIVER</ModuleType>\r
+    <GuidValue>94734718-0BBC-47fb-96A5-EE7A5AE6A2AD</GuidValue>\r
+    <Version>1.0</Version>\r
+    <Abstract>Component name for module Dhcp4</Abstract>\r
+    <Description>FIX ME!</Description>\r
+    <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>\r
+    <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>\r
+    <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION   0x00000052</Specification>\r
+  </MsaHeader>\r
+  <ModuleDefinitions>\r
+    <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>\r
+    <BinaryModule>false</BinaryModule>\r
+    <OutputFileBasename>Dhcp4Dxe</OutputFileBasename>\r
+  </ModuleDefinitions>\r
+  <LibraryClassDefinitions>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>DebugLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiDriverEntryPoint</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiBootServicesTableLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>BaseLib</Keyword>\r
+    </LibraryClass>\r
+  </LibraryClassDefinitions>\r
+  <SourceFiles>\r
+    <Filename>Dhcp4Impl.h</Filename>\r
+    <Filename>Dhcp4Option.h</Filename>\r
+    <Filename>Dhcp4Option.c</Filename>\r
+    <Filename>Dhcp4Driver.c</Filename>\r
+    <Filename>Dhcp4Driver.h</Filename>\r
+    <Filename>ComponentName.c</Filename>\r
+    <Filename>Dhcp4Io.h</Filename>\r
+    <Filename>Dhcp4Io.c</Filename>\r
+    <Filename>Dhcp4Impl.c</Filename>\r
+  </SourceFiles>\r
+  <PackageDependencies>\r
+    <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>\r
+    <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>\r
+  </PackageDependencies>\r
+  <Protocols>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiDhcp4ProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiUdp4ProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiDhcp4ServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiUdp4ServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+  </Protocols>\r
+  <Externs>\r
+    <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>\r
+    <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>\r
+    <Extern>\r
+      <ModuleEntryPoint>Dhcp4DriverEntryPoint</ModuleEntryPoint>\r
+    </Extern>\r
+  </Externs>\r
+</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 (file)
index 0000000..68da959
--- /dev/null
@@ -0,0 +1,914 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Dhcp4Impl.c\r
+\r
+Abstract:\r
+\r
+  This file implement the EFI_DHCP4_PROTOCOL interface.\r
+\r
+\r
+**/\r
+\r
+\r
+#include "Dhcp4Impl.h"\r
+\r
+\r
+/**\r
+  Get the current operation parameter and lease for the network interface.\r
+\r
+  @param  This                   The DHCP protocol instance\r
+  @param  Dhcp4ModeData          The variable to save the DHCP mode data.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid\r
+  @retval EFI_SUCCESS            The Dhcp4ModeData is updated with the current\r
+                                 operation parameter.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4GetModeData (\r
+  IN  EFI_DHCP4_PROTOCOL    *This,\r
+  OUT EFI_DHCP4_MODE_DATA   *Dhcp4ModeData\r
+  )\r
+{\r
+  DHCP_PROTOCOL             *Instance;\r
+  DHCP_SERVICE              *DhcpSb;\r
+  DHCP_PARAMETER            *Para;\r
+  EFI_TPL                   OldTpl;\r
+  IP4_ADDR                  Ip;\r
+\r
+  //\r
+  // First validate the parameters.\r
+  //\r
+  if ((This == NULL) || (Dhcp4ModeData == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP_INSTANCE_FROM_THIS (This);\r
+\r
+  if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl  = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  DhcpSb  = Instance->Service;\r
+\r
+  //\r
+  // Caller can use GetModeData to retrieve current DHCP states\r
+  // no matter whether it is the active child or not.\r
+  //\r
+  Dhcp4ModeData->State                     = DhcpSb->DhcpState;\r
+  CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+  CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (EFI_MAC_ADDRESS));\r
+\r
+  Ip = HTONL (DhcpSb->ClientAddr);\r
+  NetCopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Ip = HTONL (DhcpSb->Netmask);\r
+  NetCopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Ip = HTONL (DhcpSb->ServerAddr);\r
+  NetCopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Para = DhcpSb->Para;\r
+\r
+  if (Para != NULL) {\r
+    Ip = HTONL (Para->Router);\r
+    NetCopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+    Dhcp4ModeData->LeaseTime               = Para->Lease;\r
+  } else {\r
+    NetZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    Dhcp4ModeData->LeaseTime               = 0xffffffff;\r
+  }\r
+\r
+  Dhcp4ModeData->ReplyPacket = DhcpSb->Selected;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Free the resource related to the configure parameters.\r
+  DHCP driver will make a copy of the user's configure\r
+  such as the time out value.\r
+\r
+  @param  Config                 The DHCP configure data\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+DhcpCleanConfigure (\r
+  IN EFI_DHCP4_CONFIG_DATA  *Config\r
+  )\r
+{\r
+  UINT32                    Index;\r
+\r
+  if (Config->DiscoverTimeout != NULL) {\r
+    NetFreePool (Config->DiscoverTimeout);\r
+  }\r
+\r
+  if (Config->RequestTimeout != NULL) {\r
+    NetFreePool (Config->RequestTimeout);\r
+  }\r
+\r
+  if (Config->OptionList != NULL) {\r
+    for (Index = 0; Index < Config->OptionCount; Index++) {\r
+      if (Config->OptionList[Index] != NULL) {\r
+        NetFreePool (Config->OptionList[Index]);\r
+      }\r
+    }\r
+\r
+    NetFreePool (Config->OptionList);\r
+  }\r
+\r
+  NetZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+}\r
+\r
+\r
+/**\r
+  Allocate memory for configure parameter such as timeout value for Dst,\r
+  then copy the configure parameter from Src to Dst.\r
+\r
+  @param  Dst                    The destination DHCP configure data.\r
+  @param  Src                    The source DHCP configure data.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.\r
+  @retval EFI_SUCCESS            The configure is copied.\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpCopyConfigure (\r
+  IN EFI_DHCP4_CONFIG_DATA  *Dst,\r
+  IN EFI_DHCP4_CONFIG_DATA  *Src\r
+  )\r
+{\r
+  EFI_DHCP4_PACKET_OPTION   **DstOptions;\r
+  EFI_DHCP4_PACKET_OPTION   **SrcOptions;\r
+  INTN                      Len;\r
+  UINT32                    Index;\r
+\r
+  CopyMem (Dst, Src, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+  Dst->DiscoverTimeout  = NULL;\r
+  Dst->RequestTimeout   = NULL;\r
+  Dst->OptionList       = NULL;\r
+\r
+  //\r
+  // Allocate a memory then copy DiscoverTimeout to it\r
+  //\r
+  if (Src->DiscoverTimeout != NULL) {\r
+    Len                   = Src->DiscoverTryCount * sizeof (UINT32);\r
+    Dst->DiscoverTimeout  = NetAllocatePool (Len);\r
+\r
+    if (Dst->DiscoverTimeout == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    for (Index = 0; Index < Src->DiscoverTryCount; Index++) {\r
+      Dst->DiscoverTimeout[Index] = NET_MAX (Src->DiscoverTimeout[Index], 1);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Allocate a memory then copy RequestTimeout to it\r
+  //\r
+  if (Src->RequestTimeout != NULL) {\r
+    Len                 = Src->RequestTryCount * sizeof (UINT32);\r
+    Dst->RequestTimeout = NetAllocatePool (Len);\r
+\r
+    if (Dst->RequestTimeout == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    for (Index = 0; Index < Src->RequestTryCount; Index++) {\r
+      Dst->RequestTimeout[Index] = NET_MAX (Src->RequestTimeout[Index], 1);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Allocate an array of dhcp option point, then allocate memory\r
+  // for each option and copy the source option to it\r
+  //\r
+  if (Src->OptionList != NULL) {\r
+    Len             = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *);\r
+    Dst->OptionList = NetAllocateZeroPool (Len);\r
+\r
+    if (Dst->OptionList == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    DstOptions  = Dst->OptionList;\r
+    SrcOptions  = Src->OptionList;\r
+\r
+    for (Index = 0; Index < Src->OptionCount; Index++) {\r
+      Len = sizeof (EFI_DHCP4_PACKET_OPTION) + NET_MAX (SrcOptions[Index]->Length - 1, 0);\r
+\r
+      DstOptions[Index] = NetAllocatePool (Len);\r
+\r
+      if (DstOptions[Index] == NULL) {\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      NetCopyMem (DstOptions[Index], SrcOptions[Index], Len);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  DhcpCleanConfigure (Dst);\r
+  return EFI_OUT_OF_RESOURCES;\r
+}\r
+\r
+\r
+/**\r
+  Give up the control of the DHCP service to let other child\r
+  resume. Don't change the service's DHCP state and the Client\r
+  address and option list configure as required by RFC2131.\r
+\r
+  @param  DhcpSb                 The DHCP service instance.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+DhcpYieldControl (\r
+  IN DHCP_SERVICE           *DhcpSb\r
+  )\r
+{\r
+  EFI_DHCP4_CONFIG_DATA     *Config;\r
+  DHCP_PROTOCOL             *Instance;\r
+\r
+  Instance  = DhcpSb->ActiveChild;\r
+  Config    = &DhcpSb->ActiveConfig;\r
+\r
+  DhcpSb->ServiceState  = DHCP_UNCONFIGED;\r
+  DhcpSb->ActiveChild   = NULL;\r
+\r
+  if (Config->DiscoverTimeout != NULL) {\r
+    NetFreePool (Config->DiscoverTimeout);\r
+\r
+    Config->DiscoverTryCount  = 0;\r
+    Config->DiscoverTimeout   = NULL;\r
+  }\r
+\r
+  if (Config->RequestTimeout != NULL) {\r
+    NetFreePool (Config->RequestTimeout);\r
+\r
+    Config->RequestTryCount = 0;\r
+    Config->RequestTimeout  = NULL;\r
+  }\r
+\r
+  Config->Dhcp4Callback   = NULL;\r
+  Config->CallbackContext = NULL;\r
+}\r
+\r
+\r
+/**\r
+  Configure the DHCP protocol instance and its underlying DHCP service\r
+  for operation. If Dhcp4CfgData is NULL and the child is currently\r
+  controlling the DHCP service, release the control.\r
+\r
+  @param  This                   The DHCP protocol instance\r
+  @param  Dhcp4CfgData           The DHCP configure data.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameters are invalid.\r
+  @retval EFI_ACCESS_DENIED      The service isn't in one of configurable states,\r
+                                 or there is already an active child.\r
+  @retval EFI_OUT_OF_RESOURCE    Failed to allocate some resources.\r
+  @retval EFI_SUCCESS            The child is configured.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4Configure (\r
+  IN EFI_DHCP4_PROTOCOL     *This,\r
+  IN EFI_DHCP4_CONFIG_DATA  *Dhcp4CfgData       OPTIONAL\r
+  )\r
+{\r
+  EFI_DHCP4_CONFIG_DATA     *Config;\r
+  DHCP_PROTOCOL             *Instance;\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  UINT32                    Index;\r
+  IP4_ADDR                  Ip;\r
+\r
+  //\r
+  // First validate the parameters\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Dhcp4CfgData != NULL) {\r
+    if (Dhcp4CfgData->DiscoverTryCount && (Dhcp4CfgData->DiscoverTimeout == NULL)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (Dhcp4CfgData->RequestTryCount && (Dhcp4CfgData->RequestTimeout == NULL)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (Dhcp4CfgData->OptionCount && (Dhcp4CfgData->OptionList == NULL)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    NetCopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR));\r
+\r
+    if ((Ip != 0) && !Ip4IsUnicast (NTOHL (Ip), 0)) {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  Instance = DHCP_INSTANCE_FROM_THIS (This);\r
+\r
+  if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl  = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  DhcpSb  = Instance->Service;\r
+  Config  = &DhcpSb->ActiveConfig;\r
+\r
+  Status  = EFI_ACCESS_DENIED;\r
+\r
+  if ((DhcpSb->DhcpState != Dhcp4Stopped) &&\r
+      (DhcpSb->DhcpState != Dhcp4Init) &&\r
+      (DhcpSb->DhcpState != Dhcp4InitReboot) &&\r
+      (DhcpSb->DhcpState != Dhcp4Bound)) {\r
+\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (Dhcp4CfgData != NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    DhcpCleanConfigure (Config);\r
+\r
+    if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    DhcpSb->UserOptionLen = 0;\r
+\r
+    for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {\r
+      DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;\r
+    }\r
+\r
+    DhcpSb->ActiveChild = Instance;\r
+\r
+    if (DhcpSb->DhcpState == Dhcp4Stopped) {\r
+      DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);\r
+\r
+      if (DhcpSb->ClientAddr != 0) {\r
+        DhcpSb->DhcpState = Dhcp4InitReboot;\r
+      } else {\r
+        DhcpSb->DhcpState = Dhcp4Init;\r
+      }\r
+    }\r
+\r
+    DhcpSb->ServiceState  = DHCP_CONFIGED;\r
+    Status                = EFI_SUCCESS;\r
+\r
+  } else if (DhcpSb->ActiveChild == Instance) {\r
+    Status = EFI_SUCCESS;\r
+    DhcpYieldControl (DhcpSb);\r
+  }\r
+\r
+ON_EXIT:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start the DHCP process.\r
+\r
+  @param  This                   The DHCP protocol instance\r
+  @param  CompletionEvent        The event to signal is address is acquired.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameters are invalid.\r
+  @retval EFI_NOT_STARTED        The protocol hasn't been configured.\r
+  @retval EFI_ALREADY_STARTED    The DHCP process has already been started.\r
+  @retval EFI_SUCCESS            The DHCP process is started.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4Start (\r
+  IN EFI_DHCP4_PROTOCOL     *This,\r
+  IN EFI_EVENT              CompletionEvent   OPTIONAL\r
+  )\r
+{\r
+  DHCP_PROTOCOL             *Instance;\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  //\r
+  // First validate the parameters\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP_INSTANCE_FROM_THIS (This);\r
+\r
+  if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl  = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  DhcpSb  = Instance->Service;\r
+\r
+  if (DhcpSb->DhcpState == Dhcp4Stopped) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {\r
+    Status = EFI_ALREADY_STARTED;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  DhcpSb->IoStatus = EFI_ALREADY_STARTED;\r
+\r
+  if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Start/Restart the receiving.\r
+  //\r
+  Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Instance->CompletionEvent = CompletionEvent;\r
+\r
+  //\r
+  // Restore the TPL now, don't call poll function at NET_TPL_LOCK.\r
+  //\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  if (CompletionEvent == NULL) {\r
+    while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {\r
+      DhcpSb->UdpIo->Udp->Poll (DhcpSb->UdpIo->Udp);\r
+    }\r
+\r
+    return DhcpSb->IoStatus;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Request an extra manual renew/rebind.\r
+\r
+  @param  This                   The DHCP protocol instance\r
+  @param  RebindRequest          TRUE if request a rebind, otherwise renew it\r
+  @param  CompletionEvent        Event to signal when complete\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameters are invalid\r
+  @retval EFI_NOT_STARTED        The DHCP protocol hasn't been started.\r
+  @retval EFI_ACCESS_DENIED      The DHCP protocol isn't in Bound state.\r
+  @retval EFI_SUCCESS            The DHCP is renewed/rebound.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4RenewRebind (\r
+  IN EFI_DHCP4_PROTOCOL     *This,\r
+  IN BOOLEAN                RebindRequest,\r
+  IN EFI_EVENT              CompletionEvent   OPTIONAL\r
+  )\r
+{\r
+  DHCP_PROTOCOL             *Instance;\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  //\r
+  // First validate the parameters\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP_INSTANCE_FROM_THIS (This);\r
+\r
+  if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl  = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  DhcpSb  = Instance->Service;\r
+\r
+  if (DhcpSb->DhcpState == Dhcp4Stopped) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (DhcpSb->DhcpState != Dhcp4Bound) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (DHCP_IS_BOOTP (DhcpSb->Para)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Transit the states then send a extra DHCP request\r
+  //\r
+  if (!RebindRequest) {\r
+    DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);\r
+  } else {\r
+    DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);\r
+  }\r
+\r
+  Status = DhcpSendMessage (\r
+             DhcpSb,\r
+             DhcpSb->Selected,\r
+             DhcpSb->Para,\r
+             DHCP_MSG_REQUEST,\r
+             "Extra renew/rebind by the application"\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  DhcpSb->ExtraRefresh        = TRUE;\r
+  DhcpSb->IoStatus            = EFI_ALREADY_STARTED;\r
+  Instance->RenewRebindEvent  = CompletionEvent;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  if (CompletionEvent == NULL) {\r
+    while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {\r
+      DhcpSb->UdpIo->Udp->Poll (DhcpSb->UdpIo->Udp);\r
+    }\r
+\r
+    return DhcpSb->IoStatus;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Release the current acquired lease.\r
+\r
+  @param  This                   The DHCP protocol instance\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid\r
+  @retval EFI_DEVICE_ERROR       Failed to transmit the DHCP release packet\r
+  @retval EFI_ACCESS_DENIED      The DHCP service isn't in one of the connected\r
+                                 state.\r
+  @retval EFI_SUCCESS            The lease is released.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4Release (\r
+  IN EFI_DHCP4_PROTOCOL     *This\r
+  )\r
+{\r
+  DHCP_PROTOCOL             *Instance;\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  //\r
+  // First validate the parameters\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP_INSTANCE_FROM_THIS (This);\r
+\r
+  if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status  = EFI_SUCCESS;\r
+  OldTpl  = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  DhcpSb  = Instance->Service;\r
+\r
+  if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {\r
+    Status = DhcpSendMessage (\r
+               DhcpSb,\r
+               DhcpSb->Selected,\r
+               DhcpSb->Para,\r
+               DHCP_MSG_RELEASE,\r
+               NULL\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  DhcpCleanLease (DhcpSb);\r
+\r
+ON_EXIT:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop the current DHCP process. After this, other DHCP child\r
+  can gain control of the service, configure and use it.\r
+\r
+  @param  This                   The DHCP protocol instance\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid.\r
+  @retval EFI_SUCCESS            The DHCP process is stopped.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4Stop (\r
+  IN EFI_DHCP4_PROTOCOL     *This\r
+  )\r
+{\r
+  DHCP_PROTOCOL             *Instance;\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  //\r
+  // First validate the parameters\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP_INSTANCE_FROM_THIS (This);\r
+\r
+  if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl  = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  DhcpSb  = Instance->Service;\r
+\r
+  DhcpCleanLease (DhcpSb);\r
+\r
+  DhcpSb->DhcpState     = Dhcp4Stopped;\r
+  DhcpSb->ServiceState  = DHCP_UNCONFIGED;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Build a new DHCP packet from the seed packet. Options may be deleted or\r
+  appended. The caller should free the NewPacket when finished using it.\r
+\r
+  @param  This                   The DHCP protocol instance.\r
+  @param  SeedPacket             The seed packet to start with\r
+  @param  DeleteCount            The number of options to delete\r
+  @param  DeleteList             The options to delete from the packet\r
+  @param  AppendCount            The number of options to append\r
+  @param  AppendList             The options to append to the packet\r
+  @param  NewPacket              The new packet, allocated and built by this\r
+                                 function.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory\r
+  @retval EFI_SUCCESS            The packet is build.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4Build (\r
+  IN EFI_DHCP4_PROTOCOL       *This,\r
+  IN EFI_DHCP4_PACKET         *SeedPacket,\r
+  IN UINT32                   DeleteCount,\r
+  IN UINT8                    *DeleteList OPTIONAL,\r
+  IN UINT32                   AppendCount,\r
+  IN EFI_DHCP4_PACKET_OPTION  *AppendList[] OPTIONAL,\r
+  OUT EFI_DHCP4_PACKET        **NewPacket\r
+  )\r
+{\r
+  //\r
+  // First validate the parameters\r
+  //\r
+  if ((This == NULL) || (NewPacket == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||\r
+      EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {\r
+\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (((DeleteCount == 0) && (AppendCount == 0)) ||\r
+      ((DeleteCount != 0) && (DeleteList == NULL)) ||\r
+      ((AppendCount != 0) && (AppendList == NULL))) {\r
+\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return DhcpBuild (\r
+           SeedPacket,\r
+           DeleteCount,\r
+           DeleteList,\r
+           AppendCount,\r
+           AppendList,\r
+           NewPacket\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Transmit and receive a packet through this DHCP service.\r
+  This is unsupported.\r
+\r
+  @param  This                   The DHCP protocol instance\r
+  @param  Token                  The transmit and receive instance\r
+\r
+  @retval EFI_UNSUPPORTED        It always returns unsupported.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4TransmitReceive (\r
+  IN EFI_DHCP4_PROTOCOL                *This,\r
+  IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token\r
+  )\r
+{\r
+  //\r
+  // This function is for PXE, leave it for now\r
+  //\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+\r
+/**\r
+  Callback function for DhcpIterateOptions. This callback sets the\r
+  EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point\r
+  the individual DHCP option in the packet.\r
+\r
+  @param  Tag                    The DHCP option type\r
+  @param  Len                    length of the DHCP option data\r
+  @param  Data                   The DHCP option data\r
+  @param  Context                The context, to pass several parameters in.\r
+\r
+  @retval EFI_SUCCESS            It always returns EFI_SUCCESS\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Dhcp4ParseCheckOption (\r
+  IN UINT8                  Tag,\r
+  IN UINT8                  Len,\r
+  IN UINT8                  *Data,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  DHCP_PARSE_CONTEXT        *Parse;\r
+\r
+  Parse = (DHCP_PARSE_CONTEXT *) Context;\r
+  Parse->Index++;\r
+\r
+  if (Parse->Index < Parse->OptionCount) {\r
+    //\r
+    // Use _CR to get the memory position of EFI_DHCP4_PACKET_OPTION for\r
+    // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only\r
+    // pass in the point to option data.\r
+    //\r
+    Parse->Option[Parse->Index - 1] = _CR (Data, EFI_DHCP4_PACKET_OPTION, Data);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the DHCP options in the Packet into the PacketOptionList.\r
+  User should allocate this array of EFI_DHCP4_PACKET_OPTION points.\r
+\r
+  @param  This                   The DHCP protocol instance\r
+  @param  Packet                 The DHCP packet to parse\r
+  @param  OptionCount            On input, the size of the PacketOptionList; On\r
+                                 output,  the actual number of options processed.\r
+  @param  PacketOptionList       The array of EFI_DHCP4_PACKET_OPTION points\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameters are invalid.\r
+  @retval EFI_BUFFER_TOO_SMALL   A bigger array of points is needed.\r
+  @retval EFI_SUCCESS            The options are parsed.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp4Parse (\r
+  IN EFI_DHCP4_PROTOCOL       *This,\r
+  IN EFI_DHCP4_PACKET         *Packet,\r
+  IN OUT UINT32               *OptionCount,\r
+  OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL\r
+  )\r
+{\r
+  DHCP_PARSE_CONTEXT        Context;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // First validate the parameters\r
+  //\r
+  if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||\r
+      (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||\r
+      EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {\r
+\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((*OptionCount != 0) && (PacketOptionList == NULL)) {\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  NetZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));\r
+\r
+  Context.Option      = PacketOptionList;\r
+  Context.OptionCount = *OptionCount;\r
+  Context.Index       = 0;\r
+\r
+  Status              = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  *OptionCount = Context.Index;\r
+\r
+  if (Context.Index > Context.OptionCount) {\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_DHCP4_PROTOCOL  mDhcp4ProtocolTemplate = {\r
+  EfiDhcp4GetModeData,\r
+  EfiDhcp4Configure,\r
+  EfiDhcp4Start,\r
+  EfiDhcp4RenewRebind,\r
+  EfiDhcp4Release,\r
+  EfiDhcp4Stop,\r
+  EfiDhcp4Build,\r
+  EfiDhcp4TransmitReceive,\r
+  EfiDhcp4Parse\r
+};\r
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h
new file mode 100644 (file)
index 0000000..7f44941
--- /dev/null
@@ -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__
+
+
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Udp4.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#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 (file)
index 0000000..e0fdcd3
--- /dev/null
@@ -0,0 +1,1631 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Dhcp4Io.c\r
+\r
+Abstract:\r
+\r
+  EFI DHCP protocol implementation\r
+\r
+\r
+**/\r
+\r
+\r
+#include "Dhcp4Impl.h"\r
+\r
+UINT32  mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 };\r
+\r
+\r
+/**\r
+  Send an initial DISCOVER or REQUEST message according to the\r
+  DHCP service's current state.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+\r
+  @retval EFI_SUCCESS           The request has been sent\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpInitRequest (\r
+  IN DHCP_SERVICE           *DhcpSb\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot));\r
+\r
+  if (DhcpSb->DhcpState == Dhcp4Init) {\r
+    DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE);\r
+    Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      DhcpSb->DhcpState = Dhcp4Init;\r
+      return Status;\r
+    }\r
+\r
+    DhcpSb->WaitOffer = DHCP_WAIT_OFFER;\r
+  } else {\r
+    DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE);\r
+    Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      DhcpSb->DhcpState = Dhcp4InitReboot;\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Call user provided callback function, and return the value the\r
+  function returns. If the user doesn't provide a callback, a\r
+  proper return value is selected to let the caller continue the\r
+  normal process.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  Event                 The event as defined in the spec\r
+  @param  Packet                The current packet trigger the event\r
+  @param  NewPacket             The user's return new packet\r
+\r
+  @retval EFI_NOT_READY         Direct the caller to continue collecting the offer.\r
+  @retval EFI_SUCCESS           The user function returns success.\r
+  @retval EFI_ABORTED           The user function ask it to abort.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpCallUser (\r
+  IN  DHCP_SERVICE          *DhcpSb,\r
+  IN  EFI_DHCP4_EVENT       Event,\r
+  IN  EFI_DHCP4_PACKET      *Packet,      OPTIONAL\r
+  OUT EFI_DHCP4_PACKET      **NewPacket   OPTIONAL\r
+  )\r
+{\r
+  EFI_DHCP4_CONFIG_DATA     *Config;\r
+  EFI_STATUS                Status;\r
+\r
+  if (NewPacket != NULL) {\r
+    *NewPacket = NULL;\r
+  }\r
+\r
+  //\r
+  // If user doesn't provide the call back function, return the value\r
+  // that directs the client to continue the normal process.\r
+  // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting\r
+  // the offers and select a offer, EFI_NOT_READY tells the client to\r
+  // collect more offers.\r
+  //\r
+  Config = &DhcpSb->ActiveConfig;\r
+\r
+  if (Config->Dhcp4Callback == NULL) {\r
+    if (Event == Dhcp4RcvdOffer) {\r
+      return EFI_NOT_READY;\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Status = Config->Dhcp4Callback (\r
+                     &DhcpSb->ActiveChild->Dhcp4Protocol,\r
+                     Config->CallbackContext,\r
+                     DhcpSb->DhcpState,\r
+                     Event,\r
+                     Packet,\r
+                     NewPacket\r
+                     );\r
+\r
+  //\r
+  // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,\r
+  // and EFI_ABORTED. If it returns values other than those, assume\r
+  // it to be EFI_ABORTED.\r
+  //\r
+  if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) {\r
+    return Status;\r
+  }\r
+\r
+  return EFI_ABORTED;\r
+}\r
+\r
+\r
+/**\r
+  Notify the user about the operation result.\r
+\r
+  @param  DhcpSb                DHCP service instance\r
+  @param  Which                 which notify function to signal\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+DhcpNotifyUser (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN INTN                   Which\r
+  )\r
+{\r
+  DHCP_PROTOCOL             *Child;\r
+\r
+  if ((Child = DhcpSb->ActiveChild) == NULL) {\r
+    return ;\r
+  }\r
+\r
+  if ((Child->CompletionEvent != NULL) &&\r
+     ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))) {\r
+\r
+    gBS->SignalEvent (Child->CompletionEvent);\r
+    Child->CompletionEvent = NULL;\r
+  }\r
+\r
+  if ((Child->RenewRebindEvent != NULL) &&\r
+     ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))) {\r
+\r
+    gBS->SignalEvent (Child->RenewRebindEvent);\r
+    Child->RenewRebindEvent = NULL;\r
+  }\r
+}\r
+\r
+\r
+\r
+/**\r
+  Set the DHCP state. If CallUser is true, it will try to notify\r
+  the user before change the state by DhcpNotifyUser. It returns\r
+  EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns\r
+  EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test\r
+  the return value of this function.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  State                 The new DHCP state to change to\r
+  @param  CallUser              Whether we need to call user\r
+\r
+  @retval EFI_SUCCESS           The state is changed\r
+  @retval EFI_ABORTED           The user asks to abort the DHCP process.\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpSetState (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN INTN                   State,\r
+  IN BOOLEAN                CallUser\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  if (CallUser) {\r
+    Status = EFI_SUCCESS;\r
+\r
+    if (State == Dhcp4Renewing) {\r
+      Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL);\r
+\r
+    } else if (State == Dhcp4Rebinding) {\r
+      Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL);\r
+\r
+    } else if (State == Dhcp4Bound) {\r
+      Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL);\r
+\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Update the retransmission timer during the state transition.\r
+  // This will clear the retry count. This is also why the rule\r
+  // first transit the state, then send packets.\r
+  //\r
+  if (State == Dhcp4Init) {\r
+    DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount;\r
+  } else {\r
+    DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount;\r
+  }\r
+\r
+  if (DhcpSb->MaxRetries == 0) {\r
+    DhcpSb->MaxRetries = 4;\r
+  }\r
+\r
+  DhcpSb->CurRetry      = 0;\r
+  DhcpSb->PacketToLive  = 0;\r
+\r
+  DhcpSb->DhcpState     = State;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Set the retransmit timer for the packet. It will select from either\r
+  the discover timeouts/request timeouts or the default timeout values.\r
+\r
+  @param  DhcpSb                The DHCP service instance.\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+DhcpSetTransmitTimer (\r
+  IN DHCP_SERVICE           *DhcpSb\r
+  )\r
+{\r
+  UINT32                    *Times;\r
+\r
+  ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);\r
+\r
+  if (DhcpSb->DhcpState == Dhcp4Init) {\r
+    Times = DhcpSb->ActiveConfig.DiscoverTimeout;\r
+  } else {\r
+    Times = DhcpSb->ActiveConfig.RequestTimeout;\r
+  }\r
+\r
+  if (Times == NULL) {\r
+    Times = mDhcp4DefaultTimeout;\r
+  }\r
+\r
+  DhcpSb->PacketToLive = Times[DhcpSb->CurRetry];\r
+}\r
+\r
+\r
+/**\r
+  Compute the lease. If the server grants a permanent lease, just\r
+  process it as a normal timeout value since the lease will last\r
+  more than 100 years.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  Para                  The DHCP parameter extracted from the server's\r
+                                response.\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+DhcpComputeLease (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN DHCP_PARAMETER         *Para\r
+  )\r
+{\r
+  ASSERT (Para != NULL);\r
+\r
+  DhcpSb->Lease = Para->Lease;\r
+  DhcpSb->T2    = Para->T2;\r
+  DhcpSb->T1    = Para->T1;\r
+\r
+  if (DhcpSb->Lease == 0) {\r
+    DhcpSb->Lease = DHCP_DEFAULT_LEASE;\r
+  }\r
+\r
+  if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) {\r
+    DhcpSb->T2 = Para->Lease - (Para->Lease >> 3);\r
+  }\r
+\r
+  if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) {\r
+    DhcpSb->T1 = DhcpSb->Lease >> 1;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Configure a UDP IO port to use the acquired lease address.\r
+  DHCP driver needs this port to unicast packet to the server\r
+  such as DHCP release.\r
+\r
+  @param  UdpIo                 The UDP IO port to configure\r
+  @param  Context               The opaque parameter to the function.\r
+\r
+  @retval EFI_SUCCESS           The UDP IO port is successfully configured.\r
+  @retval Others                It failed to configure the port.\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpConfigLeaseIoPort (\r
+  IN UDP_IO_PORT            *UdpIo,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_UDP4_CONFIG_DATA      UdpConfigData;\r
+  EFI_IPv4_ADDRESS          Subnet;\r
+  EFI_IPv4_ADDRESS          Gateway;\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_STATUS                Status;\r
+  IP4_ADDR                  Ip;\r
+\r
+  DhcpSb = (DHCP_SERVICE *) Context;\r
+\r
+  UdpConfigData.AcceptBroadcast     = FALSE;\r
+  UdpConfigData.AcceptPromiscuous   = FALSE;\r
+  UdpConfigData.AcceptAnyPort       = FALSE;\r
+  UdpConfigData.AllowDuplicatePort  = TRUE;\r
+  UdpConfigData.TypeOfService       = 0;\r
+  UdpConfigData.TimeToLive          = 64;\r
+  UdpConfigData.DoNotFragment       = FALSE;\r
+  UdpConfigData.ReceiveTimeout      = 1;\r
+  UdpConfigData.TransmitTimeout     = 0;\r
+\r
+  UdpConfigData.UseDefaultAddress   = FALSE;\r
+  UdpConfigData.StationPort         = DHCP_CLIENT_PORT;\r
+  UdpConfigData.RemotePort          = DHCP_SERVER_PORT;\r
+\r
+  Ip = HTONL (DhcpSb->ClientAddr);\r
+  NetCopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Ip = HTONL (DhcpSb->Netmask);\r
+  NetCopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  NetZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Status = UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Add a default route if received from the server.\r
+  //\r
+  if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {\r
+    NetZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+    Ip = HTONL (DhcpSb->Para->Router);\r
+    NetCopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+    UdpIo->Udp->Routes (UdpIo->Udp, FALSE, &Subnet, &Subnet, &Gateway);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Update the lease states when a new lease is acquired. It will not only\r
+  save the acquired the address and lease time, it will also create a UDP\r
+  child to provide address resolution for the address.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources.\r
+  @retval EFI_SUCCESS           The lease is recorded.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpLeaseAcquired (\r
+  IN DHCP_SERVICE           *DhcpSb\r
+  )\r
+{\r
+  INTN                      Class;\r
+\r
+  DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);\r
+\r
+  if (DhcpSb->Para != NULL) {\r
+    DhcpSb->Netmask     = DhcpSb->Para->NetMask;\r
+    DhcpSb->ServerAddr  = DhcpSb->Para->ServerId;\r
+  }\r
+\r
+  if (DhcpSb->Netmask == 0) {\r
+    Class           = NetGetIpClass (DhcpSb->ClientAddr);\r
+    DhcpSb->Netmask = mIp4AllMasks[Class << 3];\r
+  }\r
+\r
+  if (DhcpSb->LeaseIoPort != NULL) {\r
+    UdpIoFreePort (DhcpSb->LeaseIoPort);\r
+  }\r
+\r
+  //\r
+  // Create a UDP/IP child to provide ARP service for the Leased IP,\r
+  // and transmit unicast packet with it as source address. Don't\r
+  // start receive on this port, the queued packet will be timeout.\r
+  //\r
+  DhcpSb->LeaseIoPort = UdpIoCreatePort (\r
+                          DhcpSb->Controller,\r
+                          DhcpSb->Image,\r
+                          DhcpConfigLeaseIoPort,\r
+                          DhcpSb\r
+                          );\r
+\r
+  if (DhcpSb->LeaseIoPort == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  if (!DHCP_IS_BOOTP (DhcpSb->Para)) {\r
+    DhcpComputeLease (DhcpSb, DhcpSb->Para);\r
+  }\r
+\r
+  return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);\r
+}\r
+\r
+\r
+/**\r
+  Clean up the DHCP related states, IoStatus isn't reset.\r
+\r
+  @param  DhcpSb                The DHCP instance service.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+DhcpCleanLease (\r
+  IN DHCP_SERVICE           *DhcpSb\r
+  )\r
+{\r
+  DhcpSb->DhcpState   = Dhcp4Init;\r
+  DhcpSb->Xid         = DhcpSb->Xid + 1;\r
+  DhcpSb->ClientAddr  = 0;\r
+  DhcpSb->ServerAddr  = 0;\r
+\r
+  if (DhcpSb->LastOffer != NULL) {\r
+    NetFreePool (DhcpSb->LastOffer);\r
+    DhcpSb->LastOffer = NULL;\r
+  }\r
+\r
+  if (DhcpSb->Selected != NULL) {\r
+    NetFreePool (DhcpSb->Selected);\r
+    DhcpSb->Selected = NULL;\r
+  }\r
+\r
+  if (DhcpSb->Para != NULL) {\r
+    NetFreePool (DhcpSb->Para);\r
+    DhcpSb->Para = NULL;\r
+  }\r
+\r
+  DhcpSb->Lease         = 0;\r
+  DhcpSb->T1            = 0;\r
+  DhcpSb->T2            = 0;\r
+  DhcpSb->ExtraRefresh  = FALSE;\r
+\r
+  if (DhcpSb->LeaseIoPort != NULL) {\r
+    UdpIoFreePort (DhcpSb->LeaseIoPort);\r
+    DhcpSb->LeaseIoPort = NULL;\r
+  }\r
+\r
+  if (DhcpSb->LastPacket != NULL) {\r
+    NetbufFree (DhcpSb->LastPacket);\r
+    DhcpSb->LastPacket = NULL;\r
+  }\r
+\r
+  DhcpSb->PacketToLive  = 0;\r
+  DhcpSb->CurRetry      = 0;\r
+  DhcpSb->MaxRetries    = 0;\r
+  DhcpSb->WaitOffer     = 0;\r
+  DhcpSb->LeaseLife     = 0;\r
+}\r
+\r
+\r
+/**\r
+  Select a offer among all the offers collected. If the offer selected is\r
+  of BOOTP, the lease is recorded and user notified. If the offer is of\r
+  DHCP, it will request the offer from the server.\r
+\r
+  @param  DhcpSb                The DHCP service instance.\r
+\r
+  @retval EFI_SUCCESS           One of the offer is selected.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpChooseOffer (\r
+  IN DHCP_SERVICE           *DhcpSb\r
+  )\r
+{\r
+  EFI_DHCP4_PACKET          *Selected;\r
+  EFI_DHCP4_PACKET          *NewPacket;\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT (DhcpSb->LastOffer != NULL);\r
+\r
+  //\r
+  // Stop waiting more offers\r
+  //\r
+  DhcpSb->WaitOffer = 0;\r
+\r
+  //\r
+  // User will cache previous offers if he wants to select\r
+  // from multiple offers. If user provides an invalid packet,\r
+  // use the last offer, otherwise use the provided packet.\r
+  //\r
+  NewPacket = NULL;\r
+  Status    = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Selected = DhcpSb->LastOffer;\r
+\r
+  if (NewPacket != NULL) {\r
+    if (EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {\r
+      NetFreePool (NewPacket);\r
+    } else {\r
+      NetFreePool (Selected);\r
+      Selected = NewPacket;\r
+    }\r
+  }\r
+\r
+  DhcpSb->Selected  = Selected;\r
+  DhcpSb->LastOffer = NULL;\r
+  DhcpSb->Para      = NULL;\r
+  DhcpValidateOptions (Selected, &DhcpSb->Para);\r
+\r
+  //\r
+  // A bootp offer has been selected, save the lease status,\r
+  // enter bound state then notify the user.\r
+  //\r
+  if (DHCP_IS_BOOTP (DhcpSb->Para)) {\r
+    Status = DhcpLeaseAcquired (DhcpSb);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    DhcpSb->IoStatus = EFI_SUCCESS;\r
+    DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Send a DHCP requests\r
+  //\r
+  Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);\r
+}\r
+\r
+\r
+/**\r
+  Terminate the current address acquire. All the allocated resources\r
+  are released. Be careful when calling this function. A rule related\r
+  to this is: only call DhcpEndSession at the highest level, such as\r
+  DhcpInput, DhcpOnTimerTick...At the other level, just return error.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  Status                The result of the DHCP process.\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+DhcpEndSession (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN EFI_STATUS             Status\r
+  )\r
+{\r
+  if (DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
+    DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);\r
+  } else {\r
+    DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);\r
+  }\r
+\r
+  DhcpCleanLease (DhcpSb);\r
+\r
+  DhcpSb->IoStatus = Status;\r
+  DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);\r
+}\r
+\r
+\r
+/**\r
+  Handle packets in DHCP select state.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  Packet                The DHCP packet received\r
+  @param  Para                  The DHCP parameter extracted from the packet. That\r
+                                is, all the option value that we care.\r
+\r
+  @retval EFI_SUCCESS           The packet is successfully processed.\r
+  @retval Others                Some error occured.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpHandleSelect (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN EFI_DHCP4_PACKET       *Packet,\r
+  IN DHCP_PARAMETER         *Para\r
+  )\r
+{\r
+  EFI_DHCP4_HEADER          *Head;\r
+  EFI_STATUS                Status;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // First validate the message:\r
+  // 1. the offer is a unicast\r
+  // 2. if it is a DHCP message, it must contains a server ID.\r
+  // Don't return a error for these two case otherwise the session is ended.\r
+  //\r
+  Head = &Packet->Dhcp4.Header;\r
+\r
+  if (!Ip4IsUnicast (EFI_NTOHL (Head->YourAddr), (Para == NULL ? 0 : Para->NetMask))) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!DHCP_IS_BOOTP (Para) &&\r
+     ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Call the user's callback. The action according to the return is as:\r
+  // 1. EFI_SUCESS: stop waiting for more offers, select the offer now\r
+  // 2. EFI_NOT_READY: wait for more offers\r
+  // 3. EFI_ABORTED: abort the address acquiring.\r
+  //\r
+  Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    if (DhcpSb->LastOffer != NULL) {\r
+      NetFreePool (DhcpSb->LastOffer);\r
+    }\r
+\r
+    DhcpSb->LastOffer = Packet;\r
+\r
+    return DhcpChooseOffer (DhcpSb);\r
+\r
+  } else if (Status == EFI_NOT_READY) {\r
+    if (DhcpSb->LastOffer != NULL) {\r
+      NetFreePool (DhcpSb->LastOffer);\r
+    }\r
+\r
+    DhcpSb->LastOffer = Packet;\r
+\r
+  } else if (Status == EFI_ABORTED) {\r
+    //\r
+    // DhcpInput will end the session upon error return. Remember\r
+    // only to call DhcpEndSession at the top level call.\r
+    //\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_EXIT:\r
+  NetFreePool (Packet);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Handle packets in DHCP request state.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  Packet                The DHCP packet received\r
+  @param  Para                  The DHCP parameter extracted from the packet. That\r
+                                is, all the option value that we care.\r
+\r
+  @retval EFI_SUCCESS           The packet is successfully processed.\r
+  @retval Others                Some error occured.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpHandleRequest (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN EFI_DHCP4_PACKET       *Packet,\r
+  IN DHCP_PARAMETER         *Para\r
+  )\r
+{\r
+  EFI_DHCP4_HEADER          *Head;\r
+  EFI_DHCP4_HEADER          *Selected;\r
+  EFI_STATUS                Status;\r
+  UINT8                     *Message;\r
+\r
+  ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));\r
+\r
+  Head      = &Packet->Dhcp4.Header;\r
+  Selected  = &DhcpSb->Selected->Dhcp4.Header;\r
+\r
+  //\r
+  // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.\r
+  //\r
+  if (DHCP_IS_BOOTP (Para) ||\r
+     (Para->ServerId != DhcpSb->Para->ServerId) ||\r
+     ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {\r
+\r
+    Status = EFI_SUCCESS;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Received a NAK, end the session no matter what the user returns\r
+  //\r
+  Status = EFI_DEVICE_ERROR;\r
+\r
+  if (Para->DhcpType == DHCP_MSG_NAK) {\r
+    DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check whether the ACK matches the selected offer\r
+  //\r
+  Message = NULL;\r
+\r
+  if (!EFI_IP4_EQUAL (Head->YourAddr, Selected->YourAddr)) {\r
+    Message = "Lease confirmed isn't the same as that in the offer";\r
+    goto REJECT;\r
+  }\r
+\r
+  Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Message = "Lease is denied upon received ACK";\r
+    goto REJECT;\r
+  }\r
+\r
+  //\r
+  // Record the lease, transit to BOUND state, then notify the user\r
+  //\r
+  Status = DhcpLeaseAcquired (DhcpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Message = "Lease is denied upon entering bound";\r
+    goto REJECT;\r
+  }\r
+\r
+  DhcpSb->IoStatus = EFI_SUCCESS;\r
+  DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);\r
+\r
+  NetFreePool (Packet);\r
+  return EFI_SUCCESS;\r
+\r
+REJECT:\r
+  DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);\r
+\r
+ON_EXIT:\r
+  NetFreePool (Packet);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Handle packets in DHCP renew/rebound state.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  Packet                The DHCP packet received\r
+  @param  Para                  The DHCP parameter extracted from the packet. That\r
+                                is, all the option value that we care.\r
+\r
+  @retval EFI_SUCCESS           The packet is successfully processed.\r
+  @retval Others                Some error occured.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpHandleRenewRebind (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN EFI_DHCP4_PACKET       *Packet,\r
+  IN DHCP_PARAMETER         *Para\r
+  )\r
+{\r
+  EFI_DHCP4_HEADER          *Head;\r
+  EFI_DHCP4_HEADER          *Selected;\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));\r
+\r
+  Head      = &Packet->Dhcp4.Header;\r
+  Selected  = &DhcpSb->Selected->Dhcp4.Header;\r
+\r
+  //\r
+  // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK\r
+  //\r
+  if (DHCP_IS_BOOTP (Para) ||\r
+     (Para->ServerId != DhcpSb->Para->ServerId) ||\r
+     ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {\r
+\r
+    Status = EFI_SUCCESS;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Received a NAK, ignore the user's return then terminate the process\r
+  //\r
+  Status = EFI_DEVICE_ERROR;\r
+\r
+  if (Para->DhcpType == DHCP_MSG_NAK) {\r
+    DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // The lease is different from the selected. Don't send a DECLINE\r
+  // since it isn't existed in the client's FSM.\r
+  //\r
+  if (!EFI_IP4_EQUAL (Head->YourAddr, Selected->YourAddr)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Record the lease, start timer for T1 and T2,\r
+  //\r
+  DhcpComputeLease (DhcpSb, Para);\r
+  DhcpSb->LeaseLife = 0;\r
+  DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);\r
+\r
+  if (DhcpSb->ExtraRefresh) {\r
+    DhcpSb->ExtraRefresh  = FALSE;\r
+\r
+    DhcpSb->IoStatus      = EFI_SUCCESS;\r
+    DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);\r
+  }\r
+\r
+ON_EXIT:\r
+  NetFreePool (Packet);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Handle packets in DHCP reboot state.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  Packet                The DHCP packet received\r
+  @param  Para                  The DHCP parameter extracted from the packet. That\r
+                                is, all the option value that we care.\r
+\r
+  @retval EFI_SUCCESS           The packet is successfully processed.\r
+  @retval Others                Some error occured.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpHandleReboot (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN EFI_DHCP4_PACKET       *Packet,\r
+  IN DHCP_PARAMETER         *Para\r
+  )\r
+{\r
+  EFI_DHCP4_HEADER          *Head;\r
+  EFI_STATUS                Status;\r
+\r
+  Head = &Packet->Dhcp4.Header;\r
+\r
+  //\r
+  // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK\r
+  //\r
+  if (DHCP_IS_BOOTP (Para) ||\r
+     ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {\r
+\r
+    Status = EFI_SUCCESS;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // If a NAK is received, transit to INIT and try again.\r
+  //\r
+  if (Para->DhcpType == DHCP_MSG_NAK) {\r
+    DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
+\r
+    DhcpSb->ClientAddr  = 0;\r
+    DhcpSb->DhcpState   = Dhcp4Init;\r
+\r
+    Status              = DhcpInitRequest (DhcpSb);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check whether the ACK matches the selected offer\r
+  //\r
+  if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // OK, get the parameter from server, record the lease\r
+  //\r
+  DhcpSb->Para = NetAllocatePool (sizeof (DHCP_PARAMETER));\r
+\r
+  if (DhcpSb->Para == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  DhcpSb->Selected  = Packet;\r
+  CopyMem (DhcpSb->Para, Para, sizeof (DHCP_PARAMETER));\r
+\r
+  Status            = DhcpLeaseAcquired (DhcpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  DhcpSb->IoStatus = EFI_SUCCESS;\r
+  DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);\r
+  return EFI_SUCCESS;\r
+\r
+ON_EXIT:\r
+  NetFreePool (Packet);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Handle the received DHCP packets. This function drivers the DHCP\r
+  state machine.\r
+\r
+  @param  UdpPacket             The UDP packets received.\r
+  @param  Points                The local/remote UDP access points\r
+  @param  IoStatus              The status of the UDP receive\r
+  @param  Context               The opaque parameter to the function.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+DhcpInput (\r
+  NET_BUF                   *UdpPacket,\r
+  UDP_POINTS                *Points,\r
+  EFI_STATUS                IoStatus,\r
+  VOID                      *Context\r
+  )\r
+{\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_DHCP4_HEADER          *Head;\r
+  EFI_DHCP4_PACKET          *Packet;\r
+  DHCP_PARAMETER            *Para;\r
+  EFI_STATUS                Status;\r
+  UINT32                    Len;\r
+\r
+  Packet  = NULL;\r
+  DhcpSb  = (DHCP_SERVICE *) Context;\r
+\r
+  //\r
+  // Don't restart receive if error occurs or DHCP is destoried.\r
+  //\r
+  if (EFI_ERROR (IoStatus)) {\r
+    return ;\r
+  } else if (DhcpSb->ServiceState == DHCP_DESTORY) {\r
+    NetbufFree (UdpPacket);\r
+    return ;\r
+  }\r
+\r
+  ASSERT (UdpPacket != NULL);\r
+\r
+  if (DhcpSb->DhcpState == Dhcp4Stopped) {\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Validate the packet received\r
+  //\r
+  if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Copy the DHCP message to a continuous memory block\r
+  //\r
+  Len     = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);\r
+  Packet  = (EFI_DHCP4_PACKET *) NetAllocatePool (Len);\r
+\r
+  if (Packet == NULL) {\r
+    goto RESTART;\r
+  }\r
+\r
+  Packet->Size    = Len;\r
+  Head            = &Packet->Dhcp4.Header;\r
+  Packet->Length  = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);\r
+\r
+  if (Packet->Length != UdpPacket->TotalSize) {\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Is this packet the answer to our packet?\r
+  //\r
+  if ((Head->OpCode != BOOTP_REPLY) ||\r
+      (NTOHL (Head->Xid) != DhcpSb->Xid) ||\r
+      !NET_MAC_EQUAL (&DhcpSb->Mac, Head->ClientHwAddr, DhcpSb->HwLen)) {\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Validate the options and retrieve the interested options\r
+  //\r
+  Para = NULL;\r
+  if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&\r
+      (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&\r
+      EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {\r
+\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Call the handler for each state. The handler should return\r
+  // EFI_SUCCESS if the process can go on no matter whether the\r
+  // packet is ignored or not. If the return is EFI_ERROR, the\r
+  // session will be terminated. Packet's ownership is handled\r
+  // over to the handlers. If operation succeeds, the handler\r
+  // must notify the user. It isn't necessary to do if EFI_ERROR\r
+  // is returned because the DhcpEndSession will notify the user.\r
+  //\r
+  Status = EFI_SUCCESS;\r
+\r
+  switch (DhcpSb->DhcpState) {\r
+  case Dhcp4Selecting:\r
+    Status = DhcpHandleSelect (DhcpSb, Packet, Para);\r
+    break;\r
+\r
+  case Dhcp4Requesting:\r
+    Status = DhcpHandleRequest (DhcpSb, Packet, Para);\r
+    break;\r
+\r
+  case Dhcp4InitReboot:\r
+  case Dhcp4Init:\r
+  case Dhcp4Bound:\r
+    //\r
+    // Ignore the packet in INITREBOOT, INIT and BOUND states\r
+    //\r
+    NetFreePool (Packet);\r
+    Status = EFI_SUCCESS;\r
+    break;\r
+\r
+  case Dhcp4Renewing:\r
+  case Dhcp4Rebinding:\r
+    Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);\r
+    break;\r
+\r
+  case Dhcp4Rebooting:\r
+    Status = DhcpHandleReboot (DhcpSb, Packet, Para);\r
+    break;\r
+  }\r
+\r
+  if (Para != NULL) {\r
+    NetFreePool (Para);\r
+  }\r
+\r
+  Packet = NULL;\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetbufFree (UdpPacket);\r
+    DhcpEndSession (DhcpSb, Status);\r
+    return ;\r
+  }\r
+\r
+RESTART:\r
+  NetbufFree (UdpPacket);\r
+\r
+  if (Packet != NULL) {\r
+    NetFreePool (Packet);\r
+  }\r
+\r
+  Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Release the packet.\r
+\r
+  @param  Arg                   The packet to release\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+DhcpReleasePacket (\r
+  IN VOID                   *Arg\r
+  )\r
+{\r
+  NetFreePool (Arg);\r
+}\r
+\r
+\r
+/**\r
+  Release the net buffer when packet is sent.\r
+\r
+  @param  UdpPacket             The UDP packets received.\r
+  @param  Points                The local/remote UDP access points\r
+  @param  IoStatus              The status of the UDP receive\r
+  @param  Context               The opaque parameter to the function.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+DhcpOnPacketSent (\r
+  NET_BUF                   *Packet,\r
+  UDP_POINTS                *Points,\r
+  EFI_STATUS                IoStatus,\r
+  VOID                      *Context\r
+  )\r
+{\r
+  NetbufFree (Packet);\r
+}\r
+\r
+\r
+\r
+/**\r
+  Build and transmit a DHCP message according to the current states.\r
+  This function implement the Table 5. of RFC 2131. Always transits\r
+  the state (as defined in Figure 5. of the same RFC) before sending\r
+  a DHCP message. The table is adjusted accordingly.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+  @param  Seed                  The seed packet which the new packet is based on\r
+  @param  Para                  The DHCP parameter of the Seed packet\r
+  @param  Type                  The message type to send\r
+  @param  Msg                   The human readable message to include in the packet\r
+                                sent.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources for the packet\r
+  @retval EFI_ACCESS_DENIED     Failed to transmit the packet through UDP\r
+  @retval EFI_SUCCESS           The message is sent\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpSendMessage (\r
+  IN DHCP_SERVICE           *DhcpSb,\r
+  IN EFI_DHCP4_PACKET       *Seed,\r
+  IN DHCP_PARAMETER         *Para,\r
+  IN UINT8                  Type,\r
+  IN UINT8                  *Msg\r
+  )\r
+{\r
+  EFI_DHCP4_CONFIG_DATA     *Config;\r
+  EFI_DHCP4_PACKET          *Packet;\r
+  EFI_DHCP4_PACKET          *NewPacket;\r
+  EFI_DHCP4_HEADER          *Head;\r
+  EFI_DHCP4_HEADER          *SeedHead;\r
+  UDP_IO_PORT               *UdpIo;\r
+  UDP_POINTS                EndPoint;\r
+  NET_BUF                   *Wrap;\r
+  NET_FRAGMENT              Frag;\r
+  EFI_STATUS                Status;\r
+  IP4_ADDR                  IpAddr;\r
+  UINT8                     *Buf;\r
+  UINT16                    MaxMsg;\r
+  UINT32                    Len;\r
+  UINT32                    Index;\r
+\r
+  //\r
+  // Allocate a big enough memory block to hold the DHCP packet\r
+  //\r
+  Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;\r
+\r
+  if (Msg != NULL) {\r
+    Len += (UINT32)AsciiStrLen (Msg);\r
+  }\r
+\r
+  Packet = NetAllocatePool (Len);\r
+\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet->Size    = Len;\r
+  Packet->Length  = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);\r
+\r
+  //\r
+  // Fill in the DHCP header fields\r
+  //\r
+  Config    = &DhcpSb->ActiveConfig;\r
+  SeedHead  = NULL;\r
+\r
+  if (Seed != NULL) {\r
+    SeedHead = &Seed->Dhcp4.Header;\r
+  }\r
+\r
+  Head = &Packet->Dhcp4.Header;\r
+  NetZeroMem (Head, sizeof (EFI_DHCP4_HEADER));\r
+\r
+  Head->OpCode       = BOOTP_REQUEST;\r
+  Head->HwType       = DhcpSb->HwType;\r
+  Head->HwAddrLen    = DhcpSb->HwLen;\r
+  Head->Xid          = HTONL (DhcpSb->Xid);\r
+  Head->Reserved     = HTONS (0x8000);  //Server, broadcast the message please.\r
+\r
+  EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);\r
+  NetCopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);\r
+\r
+  //\r
+  // Append the DHCP message type\r
+  //\r
+  Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;\r
+  Buf                 = Packet->Dhcp4.Option;\r
+  Buf                 = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type);\r
+\r
+  //\r
+  // Append the serverid option if necessary:\r
+  //   1. DHCP decline message\r
+  //   2. DHCP release message\r
+  //   3. DHCP request to confirm one lease.\r
+  //\r
+  if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||\r
+      ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))) {\r
+\r
+    ASSERT ((Para != NULL) && (Para->ServerId != 0));\r
+\r
+    IpAddr  = HTONL (Para->ServerId);\r
+    Buf     = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);\r
+  }\r
+\r
+  //\r
+  // Append the requested IP option if necessary:\r
+  //   1. DHCP request to use the previously allocated address\r
+  //   2. DHCP request to confirm one lease\r
+  //   3. DHCP decline to decline one lease\r
+  //\r
+  IpAddr = 0;\r
+\r
+  if (Type == DHCP_MSG_REQUEST) {\r
+    if (DhcpSb->DhcpState == Dhcp4Rebooting) {\r
+      IpAddr = EFI_IP4 (Config->ClientAddress);\r
+\r
+    } else if (DhcpSb->DhcpState == Dhcp4Requesting) {\r
+      ASSERT (SeedHead != NULL);\r
+      IpAddr = EFI_IP4 (SeedHead->YourAddr);\r
+    }\r
+\r
+  } else if (Type == DHCP_MSG_DECLINE) {\r
+    ASSERT (SeedHead != NULL);\r
+    IpAddr = EFI_IP4 (SeedHead->YourAddr);\r
+  }\r
+\r
+  if (IpAddr != 0) {\r
+    Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);\r
+  }\r
+\r
+  //\r
+  // Append the Max Message Length option if it isn't a DECLINE\r
+  // or RELEASE to direct the server use large messages instead of\r
+  // override the BOOTFILE and SERVER fields in the message head.\r
+  //\r
+  if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {\r
+    MaxMsg  = HTONS (0xFF00);\r
+    Buf     = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);\r
+  }\r
+\r
+  //\r
+  // Append the user's message if it isn't NULL\r
+  //\r
+  if (Msg != NULL) {\r
+    Len     = NET_MIN ((UINT32) AsciiStrLen (Msg), 255);\r
+    Buf     = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg);\r
+  }\r
+\r
+  //\r
+  // Append the user configured options\r
+  //\r
+  if (DhcpSb->UserOptionLen != 0) {\r
+    for (Index = 0; Index < Config->OptionCount; Index++) {\r
+      //\r
+      // We can't use any option other than the client ID from user\r
+      // if it is a DHCP decline or DHCP release .\r
+      //\r
+      if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&\r
+          (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) {\r
+        continue;\r
+      }\r
+\r
+      Buf = DhcpAppendOption (\r
+              Buf,\r
+              Config->OptionList[Index]->OpCode,\r
+              Config->OptionList[Index]->Length,\r
+              Config->OptionList[Index]->Data\r
+              );\r
+    }\r
+  }\r
+\r
+  *(Buf++) = DHCP_TAG_EOP;\r
+  Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);\r
+\r
+  //\r
+  // OK, the message is built, call the user to override it.\r
+  //\r
+  Status    = EFI_SUCCESS;\r
+  NewPacket = NULL;\r
+\r
+  if (Type == DHCP_MSG_DISCOVER) {\r
+    Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);\r
+\r
+  } else if (Type == DHCP_MSG_REQUEST) {\r
+    Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);\r
+\r
+  } else if (Type == DHCP_MSG_DECLINE) {\r
+    Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  if (NewPacket != NULL) {\r
+    NetFreePool (Packet);\r
+    Packet = NewPacket;\r
+  }\r
+\r
+  //\r
+  // Wrap it into a netbuf then send it.\r
+  //\r
+  Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;\r
+  Frag.Len  = Packet->Length;\r
+  Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);\r
+\r
+  if (Wrap == NULL) {\r
+    NetFreePool (Packet);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Save it as the last sent packet for retransmission\r
+  //\r
+  if (DhcpSb->LastPacket != NULL) {\r
+    NetbufFree (DhcpSb->LastPacket);\r
+  }\r
+\r
+  NET_GET_REF (Wrap);\r
+  DhcpSb->LastPacket = Wrap;\r
+  DhcpSetTransmitTimer (DhcpSb);\r
+\r
+  //\r
+  // Broadcast the message, unless we know the server address.\r
+  // Use the lease UdpIo port to send the unicast packet.\r
+  //\r
+  EndPoint.RemoteAddr = 0xffffffff;\r
+  EndPoint.LocalAddr  = 0;\r
+  EndPoint.RemotePort = DHCP_SERVER_PORT;\r
+  EndPoint.LocalPort  = DHCP_CLIENT_PORT;\r
+  UdpIo               = DhcpSb->UdpIo;\r
+\r
+  if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {\r
+    EndPoint.RemoteAddr = DhcpSb->ServerAddr;\r
+    EndPoint.LocalAddr  = DhcpSb->ClientAddr;\r
+    UdpIo               = DhcpSb->LeaseIoPort;\r
+  }\r
+\r
+  ASSERT (UdpIo != NULL);\r
+  Status = UdpIoSendDatagram (UdpIo, Wrap, &EndPoint, 0, DhcpOnPacketSent, DhcpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetbufFree (Wrap);\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Retransmit a saved packet. Only DISCOVER and REQUEST messages\r
+  will be retransmitted.\r
+\r
+  @param  DhcpSb                The DHCP service instance\r
+\r
+  @retval EFI_ACCESS_DENIED     Failed to transmit packet through UDP port\r
+  @retval EFI_SUCCESS           The packet is retransmitted.\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpRetransmit (\r
+  IN DHCP_SERVICE           *DhcpSb\r
+  )\r
+{\r
+  UDP_IO_PORT               *UdpIo;\r
+  UDP_POINTS                EndPoint;\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT (DhcpSb->LastPacket != NULL);\r
+\r
+  //\r
+  // Broadcast the message, unless we know the server address.\r
+  //\r
+  EndPoint.RemotePort = DHCP_SERVER_PORT;\r
+  EndPoint.LocalPort  = DHCP_CLIENT_PORT;\r
+  EndPoint.RemoteAddr = 0xffffffff;\r
+  EndPoint.LocalAddr  = 0;\r
+  UdpIo               = DhcpSb->UdpIo;\r
+\r
+  if (DhcpSb->DhcpState == Dhcp4Renewing) {\r
+    EndPoint.RemoteAddr = DhcpSb->ServerAddr;\r
+    EndPoint.LocalAddr  = DhcpSb->ClientAddr;\r
+    UdpIo               = DhcpSb->LeaseIoPort;\r
+  }\r
+\r
+  ASSERT (UdpIo != NULL);\r
+\r
+  NET_GET_REF (DhcpSb->LastPacket);\r
+  Status = UdpIoSendDatagram (\r
+             UdpIo,\r
+             DhcpSb->LastPacket,\r
+             &EndPoint,\r
+             0,\r
+             DhcpOnPacketSent,\r
+             DhcpSb\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NET_PUT_REF (DhcpSb->LastPacket);\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Each DHCP service has three timer. Two of them are count down timer.\r
+  One for the packet retransmission. The other is to collect the offers.\r
+  The third timer increaments the lease life which is compared to T1, T2,\r
+  and lease to determine the time to renew and rebind the lease.\r
+  DhcpOnTimerTick will be called once every second.\r
+\r
+  @param  Event                 The timer event\r
+  @param  Context               The context, which is the DHCP service instance.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+DhcpOnTimerTick (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  DHCP_SERVICE              *DhcpSb;\r
+  EFI_STATUS                Status;\r
+\r
+  DhcpSb = (DHCP_SERVICE *) Context;\r
+\r
+  //\r
+  // Check the retransmit timer first\r
+  //\r
+  if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {\r
+\r
+    if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {\r
+      //\r
+      // Still has another try\r
+      //\r
+      DhcpRetransmit (DhcpSb);\r
+      DhcpSetTransmitTimer (DhcpSb);\r
+\r
+    } else {\r
+      if (!DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
+        goto END_SESSION;\r
+      }\r
+\r
+      //\r
+      // Retransmission failed, if the DHCP request is initiated by\r
+      // user, adjust the current state according to the lease life.\r
+      // Otherwise do nothing to wait the lease to timeout\r
+      //\r
+      if (DhcpSb->ExtraRefresh) {\r
+        Status = EFI_SUCCESS;\r
+\r
+        if (DhcpSb->LeaseLife < DhcpSb->T1) {\r
+          Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);\r
+\r
+        } else if (DhcpSb->LeaseLife < DhcpSb->T2) {\r
+          Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);\r
+\r
+        } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {\r
+          Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);\r
+\r
+        } else {\r
+          goto END_SESSION;\r
+\r
+        }\r
+\r
+        DhcpSb->IoStatus = EFI_TIMEOUT;\r
+        DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);\r
+      }\r
+    }\r
+  }\r
+\r
+  if ((DhcpSb->WaitOffer > 0) && (--DhcpSb->WaitOffer == 0)) {\r
+    //\r
+    // OK, offer collection finished, select a offer\r
+    //\r
+    ASSERT (DhcpSb->DhcpState == Dhcp4Selecting);\r
+\r
+    if (DhcpSb->LastOffer == NULL) {\r
+      goto END_SESSION;\r
+    }\r
+\r
+    if (EFI_ERROR (DhcpChooseOffer (DhcpSb))) {\r
+      goto END_SESSION;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If an address has been acquired, check whether need to\r
+  // refresh or whether it has expired.\r
+  //\r
+  if (DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
+    DhcpSb->LeaseLife++;\r
+\r
+    //\r
+    // Don't timeout the lease, only count the life if user is\r
+    // requesting extra renew/rebind. Adjust the state after that.\r
+    //\r
+    if (DhcpSb->ExtraRefresh) {\r
+      return ;\r
+    }\r
+\r
+    if (DhcpSb->LeaseLife == DhcpSb->Lease) {\r
+      //\r
+      // Lease expires, end the session\r
+      //\r
+      goto END_SESSION;\r
+\r
+    } else if (DhcpSb->LeaseLife == DhcpSb->T2) {\r
+      //\r
+      // T2 expires, transit to rebinding then send a REQUEST to any server\r
+      //\r
+      if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {\r
+        goto END_SESSION;\r
+      }\r
+\r
+      Status = DhcpSendMessage (\r
+                 DhcpSb,\r
+                 DhcpSb->Selected,\r
+                 DhcpSb->Para,\r
+                 DHCP_MSG_REQUEST,\r
+                 NULL\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto END_SESSION;\r
+      }\r
+\r
+    } else if (DhcpSb->LeaseLife == DhcpSb->T1) {\r
+      //\r
+      // T1 expires, transit to renewing, then send a REQUEST to the server\r
+      //\r
+      if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {\r
+        goto END_SESSION;\r
+      }\r
+\r
+      Status = DhcpSendMessage (\r
+                 DhcpSb,\r
+                 DhcpSb->Selected,\r
+                 DhcpSb->Para,\r
+                 DHCP_MSG_REQUEST,\r
+                 NULL\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto END_SESSION;\r
+      }\r
+    }\r
+  }\r
+\r
+  return ;\r
+\r
+END_SESSION:\r
+  DhcpEndSession (DhcpSb, EFI_TIMEOUT);\r
+\r
+  return ;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h
new file mode 100644 (file)
index 0000000..1da95f9
--- /dev/null
@@ -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 (file)
index 0000000..b16f469
--- /dev/null
@@ -0,0 +1,906 @@
+/** @file\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Dhcp4Option.c\r
+\r
+Abstract:\r
+\r
+  Function to validate, parse, process the DHCP options\r
+\r
+\r
+**/\r
+\r
+#include "Dhcp4Impl.h"\r
+\r
+//\r
+// A list of the format of DHCP Options sorted by option tag\r
+// to validate a dhcp message. Refere the comments of the\r
+// DHCP_OPTION_FORMAT structure.\r
+//\r
+STATIC\r
+DHCP_OPTION_FORMAT\r
+DhcpOptionFormats [] = {\r
+  {DHCP_TAG_NETMASK,        DHCP_OPTION_IP,     1, 1  , TRUE},\r
+  {DHCP_TAG_TIME_OFFSET,    DHCP_OPTION_INT32,  1, 1  , FALSE},\r
+  {DHCP_TAG_ROUTER,         DHCP_OPTION_IP,     1, -1 , TRUE},\r
+  {DHCP_TAG_TIME_SERVER,    DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NAME_SERVER,    DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_DNS_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_LOG_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_COOKIE_SERVER,  DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_LPR_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_IMPRESS_SERVER, DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_RL_SERVER,      DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_HOSTNAME,       DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_BOOTFILE_LEN,   DHCP_OPTION_INT16,  1, 1  , FALSE},\r
+  {DHCP_TAG_DUMP,           DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_DOMAINNAME,     DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_SWAP_SERVER,    DHCP_OPTION_IP,     1, 1  , FALSE},\r
+  {DHCP_TAG_ROOTPATH,       DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_EXTEND_PATH,    DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_IPFORWARD,      DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_NONLOCAL_SRR,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_POLICY_SRR,     DHCP_OPTION_IPPAIR, 1, -1 , FALSE},\r
+  {DHCP_TAG_EMTU,           DHCP_OPTION_INT16,  1, 1  , FALSE},\r
+  {DHCP_TAG_TTL,            DHCP_OPTION_INT8,   1, 1  , FALSE},\r
+  {DHCP_TAG_PATHMTU_AGE,    DHCP_OPTION_INT32,  1, 1  , FALSE},\r
+  {DHCP_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16,  1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_IFMTU,          DHCP_OPTION_INT16,  1, 1  , FALSE},\r
+  {DHCP_TAG_SUBNET_LOCAL,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_BROADCAST,      DHCP_OPTION_IP,     1, 1  , FALSE},\r
+  {DHCP_TAG_DISCOVER_MASK,  DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_SUPPLY_MASK,    DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP,     1, 1  , FALSE},\r
+  {DHCP_TAG_STATIC_ROUTE,   DHCP_OPTION_IPPAIR, 1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_TRAILER,        DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_ARPAGE,         DHCP_OPTION_INT32,  1, 1  , FALSE},\r
+  {DHCP_TAG_ETHER_ENCAP,    DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+\r
+  {DHCP_TAG_TCP_TTL,        DHCP_OPTION_INT8,   1, 1  , FALSE},\r
+  {DHCP_TAG_KEEP_INTERVAL,  DHCP_OPTION_INT32,  1, 1  , FALSE},\r
+  {DHCP_TAG_KEEP_GARBAGE,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+\r
+  {DHCP_TAG_NIS_DOMAIN,     DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_NIS_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NTP_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_VENDOR,         DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_NBNS,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NBDD,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NBTYPE,         DHCP_OPTION_INT8,   1, 1  , FALSE},\r
+  {DHCP_TAG_NBSCOPE,        DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_XFONT,          DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_XDM,            DHCP_OPTION_IP,     1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_REQUEST_IP,     DHCP_OPTION_IP,     1, 1  , FALSE},\r
+  {DHCP_TAG_LEASE,          DHCP_OPTION_INT32,  1, 1  , TRUE},\r
+  {DHCP_TAG_OVERLOAD,       DHCP_OPTION_INT8,   1, 1  , TRUE},\r
+  {DHCP_TAG_TYPE,           DHCP_OPTION_INT8,   1, 1  , TRUE},\r
+  {DHCP_TAG_SERVER_ID,      DHCP_OPTION_IP,     1, 1  , TRUE},\r
+  {DHCP_TAG_PARA_LIST,      DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_MESSAGE,        DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_MAXMSG,         DHCP_OPTION_INT16,  1, 1  , FALSE},\r
+  {DHCP_TAG_T1,             DHCP_OPTION_INT32,  1, 1  , TRUE},\r
+  {DHCP_TAG_T2,             DHCP_OPTION_INT32,  1, 1  , TRUE},\r
+  {DHCP_TAG_VENDOR_CLASS,   DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_CLIENT_ID,      DHCP_OPTION_INT8,   2, -1 , FALSE},\r
+\r
+  {DHCP_TAG_NISPLUS,        DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_NISPLUS_SERVER, DHCP_OPTION_IP,     1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_TFTP,           DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_BOOTFILE,       DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_MOBILEIP,       DHCP_OPTION_IP,     0, -1 , FALSE},\r
+  {DHCP_TAG_SMTP,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_POP3,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NNTP,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_WWW,            DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_FINGER,         DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_IRC,            DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_STTALK,         DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_STDA,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_CLASSLESS_ROUTE,DHCP_OPTION_INT8,   5, -1 , FALSE},\r
+};\r
+\r
+\r
+/**\r
+  Binary search the DhcpOptionFormats array to find the format\r
+  information about a specific option.\r
+\r
+  @param  Tag                    The option's tag.\r
+\r
+  @return The point to the option's format, NULL if not found.\r
+\r
+**/\r
+STATIC\r
+DHCP_OPTION_FORMAT *\r
+DhcpFindOptionFormat (\r
+  IN UINT8                  Tag\r
+  )\r
+{\r
+  INTN                      Left;\r
+  INTN                      Right;\r
+  INTN                      Middle;\r
+\r
+  Left  = 0;\r
+  Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1;\r
+\r
+  while (Right >= Left) {\r
+    Middle = (Left + Right) / 2;\r
+\r
+    if (Tag == DhcpOptionFormats[Middle].Tag) {\r
+      return &DhcpOptionFormats[Middle];\r
+    }\r
+\r
+    if (Tag < DhcpOptionFormats[Middle].Tag) {\r
+      Right = Middle - 1;\r
+    } else {\r
+      Left = Middle + 1;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Validate whether a single DHCP option is valid according to its format.\r
+\r
+  @param  Format                 The option's format\r
+  @param  OptValue               The value of the option\r
+  @param  Len                    The length of the option value\r
+\r
+  @return TRUE is the option is valid, otherwise FALSE.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+DhcpOptionIsValid (\r
+  IN DHCP_OPTION_FORMAT     *Format,\r
+  IN UINT8                  *OptValue,\r
+  IN INTN                   Len\r
+  )\r
+{\r
+  INTN                      Unit;\r
+  INTN                      Occur;\r
+  INTN                      Index;\r
+\r
+  Unit = 0;\r
+\r
+  switch (Format->Type) {\r
+  case DHCP_OPTION_SWITCH:\r
+  case DHCP_OPTION_INT8:\r
+    Unit = 1;\r
+    break;\r
+\r
+  case DHCP_OPTION_INT16:\r
+    Unit = 2;\r
+    break;\r
+\r
+  case DHCP_OPTION_INT32:\r
+  case DHCP_OPTION_IP:\r
+    Unit = 4;\r
+    break;\r
+\r
+  case DHCP_OPTION_IPPAIR:\r
+    Unit = 8;\r
+    break;\r
+  }\r
+\r
+  ASSERT (Unit != 0);\r
+\r
+  //\r
+  // Validate that the option appears in the full units.\r
+  //\r
+  if ((Len % Unit) != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Validate the occurance of the option unit is with in [MinOccur, MaxOccur]\r
+  //\r
+  Occur = Len / Unit;\r
+\r
+  if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) ||\r
+      ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // If the option is of type switch, only 0/1 are valid values.\r
+  //\r
+  if (Format->Type == DHCP_OPTION_SWITCH) {\r
+    for (Index = 0; Index < Occur; Index++) {\r
+      if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) {\r
+        return FALSE;\r
+      }\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Extract the client interested options, all the parameters are\r
+  converted to host byte order.\r
+\r
+  @param  Tag                    The DHCP option tag\r
+  @param  Len                    The length of the option\r
+  @param  Data                   The value of the DHCP option\r
+  @param  Para                   The variable to save the interested parameter\r
+\r
+  @retval EFI_SUCCESS            The DHCP option is successfully extracted.\r
+  @retval EFI_INVALID_PARAMETER  The DHCP option is mal-formated\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpGetParameter (\r
+  IN UINT8                  Tag,\r
+  IN INTN                   Len,\r
+  IN UINT8                  *Data,\r
+  IN DHCP_PARAMETER         *Para\r
+  )\r
+{\r
+  switch (Tag) {\r
+  case DHCP_TAG_NETMASK:\r
+    Para->NetMask = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_ROUTER:\r
+    //\r
+    // Return the first router to consumer which is the preferred one\r
+    //\r
+    Para->Router = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_LEASE:\r
+    Para->Lease = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_OVERLOAD:\r
+    Para->Overload = *Data;\r
+\r
+    if ((Para->Overload < 1) || (Para->Overload > 3)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    break;\r
+\r
+  case DHCP_TAG_TYPE:\r
+    Para->DhcpType = *Data;\r
+\r
+    if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    break;\r
+\r
+  case DHCP_TAG_SERVER_ID:\r
+    Para->ServerId = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_T1:\r
+    Para->T1 = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_T2:\r
+    Para->T2 = NetGetUint32 (Data);\r
+    break;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Inspect all the options in a single buffer. DHCP options may be contained\r
+  in several buffers, such as the BOOTP options filed, boot file or server\r
+  name. Each option buffer is required to end with DHCP_TAG_EOP.\r
+\r
+  @param  Buffer                 The buffer which contains DHCP options\r
+  @param  BufLen                 The length of the buffer\r
+  @param  Check                  The callback function for each option found\r
+  @param  Context                The opaque parameter for the Check\r
+  @param  Overload               variable to save the value of DHCP_TAG_OVERLOAD\r
+                                 option.\r
+\r
+  @retval EFI_SUCCESS            All the options are valid\r
+  @retval EFI_INVALID_PARAMETER  The options are mal-formated.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpIterateBufferOptions (\r
+  IN  UINT8                 *Buffer,\r
+  IN  INTN                  BufLen,\r
+  IN  DHCP_CHECK_OPTION     Check,            OPTIONAL\r
+  IN  VOID                  *Context,\r
+  OUT UINT8                 *Overload         OPTIONAL\r
+  )\r
+{\r
+  INTN                      Cur;\r
+  UINT8                     Tag;\r
+  UINT8                     Len;\r
+\r
+  Cur = 0;\r
+\r
+  while (Cur < BufLen) {\r
+    Tag = Buffer[Cur];\r
+\r
+    if (Tag == DHCP_TAG_PAD) {\r
+      Cur++;\r
+      continue;\r
+    } else if (Tag == DHCP_TAG_EOP) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    Cur++;\r
+\r
+    if (Cur == BufLen) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Len = Buffer[Cur++];\r
+\r
+    if (Cur + Len > BufLen) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if ((Tag == DHCP_TAG_OVERLOAD) && (Overload != NULL)) {\r
+      if (Len != 1) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      *Overload = Buffer[Cur];\r
+    }\r
+\r
+    if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Cur += Len;\r
+  }\r
+\r
+  //\r
+  // Each option buffer is expected to end with an EOP\r
+  //\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+\r
+/**\r
+  Iterate through a DHCP message to visit each option. First inspect\r
+  all the options in the OPTION field. Then if overloaded, inspect\r
+  the options in FILENAME and SERVERNAME fields. One option may be\r
+  encoded in several places. See RFC 3396 Encoding Long Options in DHCP\r
+\r
+  @param  Packet                 The DHCP packet to check the options for\r
+  @param  Check                  The callback function to be called for each option\r
+                                 found\r
+  @param  Context                The opaque parameter for Check\r
+\r
+  @retval EFI_SUCCESS            The DHCP packet's options are well formated\r
+  @retval Others                 The DHCP packet's options are not well formated\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpIterateOptions (\r
+  IN  EFI_DHCP4_PACKET      *Packet,\r
+  IN  DHCP_CHECK_OPTION     Check,        OPTIONAL\r
+  IN  VOID                  *Context\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  UINT8                     Overload;\r
+\r
+  Overload = 0;\r
+\r
+  Status   = DhcpIterateBufferOptions (\r
+               Packet->Dhcp4.Option,\r
+               Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32),\r
+               Check,\r
+               Context,\r
+               &Overload\r
+               );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) {\r
+    Status = DhcpIterateBufferOptions (\r
+               Packet->Dhcp4.Header.BootFileName,\r
+               128,\r
+               Check,\r
+               Context,\r
+               NULL\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) {\r
+    Status = DhcpIterateBufferOptions (\r
+               Packet->Dhcp4.Header.ServerName,\r
+               64,\r
+               Check,\r
+               Context,\r
+               NULL\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Call back function to DhcpiterateOptions to compute each option's\r
+  length. It just adds the data length of all the occurances of this\r
+  Tag. Context is an array of 256 DHCP_OPTION_COUNT.\r
+\r
+  @param  Tag                    The current option to check\r
+  @param  Len                    The length of the option data\r
+  @param  Data                   The option data\r
+  @param  Context                The context, which is a array of 256\r
+                                 DHCP_OPTION_COUNT.\r
+\r
+  @retval EFI_SUCCESS            It always returns EFI_SUCCESS.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpGetOptionLen (\r
+  IN UINT8                  Tag,\r
+  IN UINT8                  Len,\r
+  IN UINT8                  *Data,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  DHCP_OPTION_COUNT         *OpCount;\r
+\r
+  OpCount             = (DHCP_OPTION_COUNT *) Context;\r
+  OpCount[Tag].Offset = OpCount[Tag].Offset + Len;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Call back function to DhcpiterateOptions to consolidate each option's\r
+  data. There are maybe several occurance of the same option.\r
+\r
+  @param  Tag                    The option to consolidate its data\r
+  @param  Len                    The length of option data\r
+  @param  Data                   The data of the option's current occurance\r
+  @param  Context                The context, which is DHCP_OPTION_CONTEXT. This\r
+                                 array is  just a wrap to pass THREE parameters.\r
+\r
+  @retval EFI_SUCCESS            It always returns EFI_SUCCESS\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpFillOption (\r
+  IN UINT8                  Tag,\r
+  IN UINT8                  Len,\r
+  IN UINT8                  *Data,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  DHCP_OPTION_CONTEXT       *OptContext;\r
+  DHCP_OPTION_COUNT         *OptCount;\r
+  DHCP_OPTION               *Options;\r
+  UINT8                     *Buf;\r
+  UINT8                     Index;\r
+\r
+  OptContext  = (DHCP_OPTION_CONTEXT *) Context;\r
+\r
+  OptCount    = OptContext->OpCount;\r
+  Index       = OptCount[Tag].Index;\r
+  Options     = OptContext->Options;\r
+  Buf         = OptContext->Buf;\r
+\r
+  if (Options[Index].Data == NULL) {\r
+    Options[Index].Tag  = Tag;\r
+    Options[Index].Data = Buf + OptCount[Tag].Offset;\r
+  }\r
+\r
+  NetCopyMem (Buf + OptCount[Tag].Offset, Data, Len);\r
+\r
+  OptCount[Tag].Offset  = OptCount[Tag].Offset + Len;\r
+  Options[Index].Len    = Options[Index].Len + Len;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the options of a DHCP packet. It supports RFC 3396: Encoding\r
+  Long Options in DHCP. That is, it will combine all the option value\r
+  of all the occurances of each option.\r
+  A little bit of implemenation:\r
+  It adopts the "Key indexed counting" algorithm. First, it allocates\r
+  an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded\r
+  as a UINT8. It then iterates the DHCP packet to get data length of\r
+  each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it\r
+  knows the number of present options and their length. It allocates a\r
+  array of DHCP_OPTION and a continous buffer after the array to put\r
+  all the options' data. Each option's data is pointed to by the Data\r
+  field in DHCP_OPTION structure. At last, it call DhcpIterateOptions\r
+  with DhcpFillOption to fill each option's data to its position in the\r
+  buffer.\r
+\r
+  @param  Packet                 The DHCP packet to parse the options\r
+  @param  Count                  The number of valid dhcp options present in the\r
+                                 packet\r
+  @param  OptionPoint            The array that contains the DHCP options. Caller\r
+                                 should free it.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to parse the packet.\r
+  @retval EFI_INVALID_PARAMETER  The options are mal-formated\r
+  @retval EFI_SUCCESS            The options are parsed into OptionPoint\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpParseOption (\r
+  IN  EFI_DHCP4_PACKET      *Packet,\r
+  OUT INTN                  *Count,\r
+  OUT DHCP_OPTION           **OptionPoint\r
+  )\r
+{\r
+  DHCP_OPTION_CONTEXT       Context;\r
+  DHCP_OPTION               *Options;\r
+  DHCP_OPTION_COUNT         *OptCount;\r
+  EFI_STATUS                Status;\r
+  UINT16                    TotalLen;\r
+  INTN                      OptNum;\r
+  INTN                      Index;\r
+\r
+  ASSERT ((Count != NULL) && (OptionPoint != NULL));\r
+\r
+  //\r
+  // First compute how many options and how long each option is\r
+  // with the "Key indexed counting" algorithms.\r
+  //\r
+  OptCount = NetAllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));\r
+\r
+  if (OptCount == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Before the loop, Offset is the length of the option. After loop,\r
+  // OptCount[Index].Offset specifies the offset into the continuous\r
+  // option value buffer to put the data.\r
+  //\r
+  TotalLen  = 0;\r
+  OptNum    = 0;\r
+\r
+  for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {\r
+    if (OptCount[Index].Offset != 0) {\r
+      OptCount[Index].Index   = (UINT8) OptNum;\r
+\r
+      TotalLen                = TotalLen + OptCount[Index].Offset;\r
+      OptCount[Index].Offset  = TotalLen - OptCount[Index].Offset;\r
+\r
+      OptNum++;\r
+    }\r
+  }\r
+\r
+  *Count        = OptNum;\r
+  *OptionPoint  = NULL;\r
+\r
+  if (OptNum == 0) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Allocate a buffer to hold the DHCP options, and after that, a\r
+  // continuous buffer to put all the options' data.\r
+  //\r
+  Options = NetAllocateZeroPool (OptNum * sizeof (DHCP_OPTION) + TotalLen);\r
+\r
+  if (Options == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Context.OpCount = OptCount;\r
+  Context.Options = Options;\r
+  Context.Buf     = (UINT8 *) (Options + OptNum);\r
+\r
+  Status          = DhcpIterateOptions (Packet, DhcpFillOption, &Context);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (Options);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  *OptionPoint = Options;\r
+\r
+ON_EXIT:\r
+  NetFreePool (OptCount);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Validate the packet's options. If necessary, allocate\r
+  and fill in the interested parameters.\r
+\r
+  @param  Packet                 The packet to validate the options\r
+  @param  Para                   The variable to save the DHCP parameters.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to validate the packet.\r
+  @retval EFI_INVALID_PARAMETER  The options are mal-formated\r
+  @retval EFI_SUCCESS            The options are parsed into OptionPoint\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpValidateOptions (\r
+  IN  EFI_DHCP4_PACKET      *Packet,\r
+  OUT DHCP_PARAMETER        **Para       OPTIONAL\r
+  )\r
+{\r
+  DHCP_PARAMETER            Parameter;\r
+  DHCP_OPTION_FORMAT        *Format;\r
+  DHCP_OPTION               *AllOption;\r
+  DHCP_OPTION               *Option;\r
+  EFI_STATUS                Status;\r
+  BOOLEAN                   Updated;\r
+  INTN                      Count;\r
+  INTN                      Index;\r
+\r
+  if (Para != NULL) {\r
+    *Para = NULL;\r
+  }\r
+\r
+  AllOption = NULL;\r
+  Status    = DhcpParseOption (Packet, &Count, &AllOption);\r
+\r
+  if (EFI_ERROR (Status) || (Count == 0)) {\r
+    return Status;\r
+  }\r
+\r
+  Updated = FALSE;\r
+  NetZeroMem (&Parameter, sizeof (Parameter));\r
+\r
+  for (Index = 0; Index < Count; Index++) {\r
+    Option = &AllOption[Index];\r
+\r
+    //\r
+    // Find the format of the option then validate it.\r
+    //\r
+    Format = DhcpFindOptionFormat (Option->Tag);\r
+\r
+    if (Format == NULL) {\r
+      continue;\r
+    }\r
+\r
+    if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    //\r
+    // Get the client interested parameters\r
+    //\r
+    if (Format->Alert && (Para != NULL)) {\r
+      Updated = TRUE;\r
+      Status  = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (Updated && (Para != NULL)) {\r
+    if ((*Para = NetAllocatePool (sizeof (DHCP_PARAMETER))) == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    CopyMem (*Para, &Parameter, sizeof (DHCP_PARAMETER));\r
+  }\r
+\r
+ON_EXIT:\r
+  NetFreePool (AllOption);\r
+  return Status;\r
+}\r
+\r
+\r
+\r
+/**\r
+  Append an option to the memory, if the option is longer than\r
+  255 bytes, splits it into several options.\r
+\r
+  @param  Buf                    The buffer to append the option to\r
+  @param  Tag                    The option's tag\r
+  @param  DataLen                The length of the option's data\r
+  @param  Data                   The option's data\r
+\r
+  @return The position to append the next option\r
+\r
+**/\r
+UINT8 *\r
+DhcpAppendOption (\r
+  IN UINT8                  *Buf,\r
+  IN UINT8                  Tag,\r
+  IN UINT16                 DataLen,\r
+  IN UINT8                  *Data\r
+  )\r
+{\r
+  INTN                      Index;\r
+  INTN                      Len;\r
+\r
+  ASSERT (DataLen != 0);\r
+\r
+  for (Index = 0; Index < (DataLen + 254) / 255; Index++) {\r
+    Len      = NET_MIN (255, DataLen - Index * 255);\r
+\r
+    *(Buf++) = Tag;\r
+    *(Buf++) = (UINT8) Len;\r
+    NetCopyMem (Buf, Data + Index * 255, Len);\r
+\r
+    Buf     += Len;\r
+  }\r
+\r
+  return Buf;\r
+}\r
+\r
+\r
+/**\r
+  Build a new DHCP packet from a seed packet. Options may be deleted or\r
+  appended. The caller should free the NewPacket when finished using it.\r
+\r
+  @param  SeedPacket             The seed packet to start with\r
+  @param  DeleteCount            The number of options to delete\r
+  @param  DeleteList             The options to delete from the packet\r
+  @param  AppendCount            The number of options to append\r
+  @param  AppendList             The options to append to the packet\r
+  @param  NewPacket              The new packet, allocated and built by this\r
+                                 function.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory\r
+  @retval EFI_SUCCESS            The packet is build.\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpBuild (\r
+  IN  EFI_DHCP4_PACKET        *SeedPacket,\r
+  IN  UINT32                  DeleteCount,\r
+  IN  UINT8                   *DeleteList     OPTIONAL,\r
+  IN  UINT32                  AppendCount,\r
+  IN  EFI_DHCP4_PACKET_OPTION *AppendList[]   OPTIONAL,\r
+  OUT EFI_DHCP4_PACKET        **NewPacket\r
+  )\r
+{\r
+  DHCP_OPTION               *Mark;\r
+  DHCP_OPTION               *SeedOptions;\r
+  EFI_DHCP4_PACKET          *Packet;\r
+  EFI_STATUS                Status;\r
+  INTN                      Count;\r
+  UINT32                    Index;\r
+  UINT32                    Len;\r
+  UINT8                     *Buf;\r
+\r
+  //\r
+  // Use an array of DHCP_OPTION to mark the existance\r
+  // and position of each valid options.\r
+  //\r
+  Mark = NetAllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);\r
+\r
+  if (Mark == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {\r
+    Mark[Index].Tag = (UINT8) Index;\r
+    Mark[Index].Len = 0;\r
+  }\r
+\r
+  //\r
+  // Get list of the options from the seed packet, then put\r
+  // them to the mark array according to their tags.\r
+  //\r
+  SeedOptions = NULL;\r
+  Status      = DhcpParseOption (SeedPacket, &Count, &SeedOptions);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  for (Index = 0; Index < (UINT32) Count; Index++) {\r
+    Mark[SeedOptions[Index].Tag] = SeedOptions[Index];\r
+  }\r
+\r
+  //\r
+  // Mark the option's length is zero if it is in the DeleteList.\r
+  //\r
+  for (Index = 0; Index < DeleteCount; Index++) {\r
+    Mark[DeleteList[Index]].Len = 0;\r
+  }\r
+\r
+  //\r
+  // Add or replace the option if it is in the append list.\r
+  //\r
+  for (Index = 0; Index < AppendCount; Index++) {\r
+    Mark[AppendList[Index]->OpCode].Len  = AppendList[Index]->Length;\r
+    Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data;\r
+  }\r
+\r
+  //\r
+  // compute the new packet length. No need to add 1 byte for\r
+  // EOP option since EFI_DHCP4_PACKET includes one extra byte\r
+  // for option. It is necessary to split the option if it is\r
+  // longer than 255 bytes.\r
+  //\r
+  Len = sizeof (EFI_DHCP4_PACKET);\r
+\r
+  for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {\r
+    if (Mark[Index].Len != 0) {\r
+      Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len;\r
+    }\r
+  }\r
+\r
+  Status  = EFI_OUT_OF_RESOURCES;\r
+  Packet  = (EFI_DHCP4_PACKET *) NetAllocatePool (Len);\r
+\r
+  if (Packet == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Packet->Size         = Len;\r
+  Packet->Length       = 0;\r
+  CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (EFI_DHCP4_HEADER));\r
+  Packet->Dhcp4.Magik  = DHCP_OPTION_MAGIC;\r
+  Buf                  = Packet->Dhcp4.Option;\r
+\r
+  for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {\r
+    if (Mark[Index].Len != 0) {\r
+      Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data);\r
+    }\r
+  }\r
+\r
+  *(Buf++)        = DHCP_TAG_EOP;\r
+  Packet->Length  = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)\r
+                      + (UINT32) (Buf - Packet->Dhcp4.Option);\r
+\r
+  *NewPacket      = Packet;\r
+  Status          = EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  if (SeedOptions != NULL) {\r
+    NetFreePool (SeedOptions);\r
+  }\r
+\r
+  NetFreePool (Mark);\r
+  return Status;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h
new file mode 100644 (file)
index 0000000..a6a485b
--- /dev/null
@@ -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
index 326fb4dc335c3b0e602a073655430a29a8602660..a1bae0f29d50af0f69700dcf3675c22d0cd9e41d 100644 (file)
@@ -590,6 +590,8 @@ Ip4ConfigOnDhcp4Complete (
   EFI_STATUS                Status;\r
   BOOLEAN                   Perment;\r
   IP4_ADDR                  Subnet;\r
   EFI_STATUS                Status;\r
   BOOLEAN                   Perment;\r
   IP4_ADDR                  Subnet;\r
+  IP4_ADDR                  Ip1;\r
+  IP4_ADDR                  Ip2;\r
 \r
   Instance = (IP4_CONFIG_INSTANCE *) Context;\r
   ASSERT (Instance->Dhcp4 != NULL);\r
 \r
   Instance = (IP4_CONFIG_INSTANCE *) Context;\r
   ASSERT (Instance->Dhcp4 != NULL);\r
@@ -641,20 +643,24 @@ Ip4ConfigOnDhcp4Complete (
     //\r
     Ip4Config->RouteTableSize    = 1;\r
 \r
     //\r
     Ip4Config->RouteTableSize    = 1;\r
 \r
-    Subnet = EFI_NTOHL (Dhcp4Mode.ClientAddress) & EFI_NTOHL (Dhcp4Mode.SubnetMask);\r
+    NetCopyMem (&Ip1, &Dhcp4Mode.ClientAddress, sizeof (IP4_ADDR));\r
+    NetCopyMem (&Ip2, &Dhcp4Mode.SubnetMask, sizeof (IP4_ADDR));\r
+    \r
+    Subnet = Ip1 & Ip2;\r
 \r
 \r
-    EFI_IP4 (Ip4Config->RouteTable[0].SubnetAddress)  = HTONL (Subnet);\r
-    Ip4Config->RouteTable[0].SubnetMask               = Dhcp4Mode.SubnetMask;\r
-    EFI_IP4 (Ip4Config->RouteTable[0].GatewayAddress) = 0;\r
+    NetCopyMem (&Ip4Config->RouteTable[0].SubnetAddress, &Subnet, sizeof (EFI_IPv4_ADDRESS));\r
+    NetCopyMem (&Ip4Config->RouteTable[0].SubnetMask, &Dhcp4Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    NetZeroMem (&Ip4Config->RouteTable[0].GatewayAddress, sizeof (EFI_IPv4_ADDRESS));\r
 \r
     //\r
     // Create a route if there is a default router.\r
     //\r
 \r
     //\r
     // Create a route if there is a default router.\r
     //\r
-    if (EFI_IP4 (Dhcp4Mode.RouterAddress) != 0) {\r
-      Ip4Config->RouteTableSize                         = 2;\r
-      EFI_IP4 (Ip4Config->RouteTable[1].SubnetAddress)  = 0;\r
-      EFI_IP4 (Ip4Config->RouteTable[1].SubnetMask)     = 0;\r
-      Ip4Config->RouteTable[1].GatewayAddress           = Dhcp4Mode.RouterAddress;\r
+    if (!EFI_IP4_EQUAL (Dhcp4Mode.RouterAddress, mZeroIp4Addr)) {\r
+      Ip4Config->RouteTableSize = 2;\r
+\r
+      NetZeroMem (&Ip4Config->RouteTable[1].SubnetAddress, sizeof (EFI_IPv4_ADDRESS));\r
+      NetZeroMem (&Ip4Config->RouteTable[1].SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+      NetCopyMem (&Ip4Config->RouteTable[1].GatewayAddress, &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
     }\r
 \r
     Instance->Result = EFI_SUCCESS;\r
     }\r
 \r
     Instance->Result = EFI_SUCCESS;\r
index 265135a1177fef45d3dd24681080498d2631cf58..3d857b047e8968f52c01fe3003a72960262b8e1f 100644 (file)
@@ -60,7 +60,6 @@ EfiIp4ConfigUnload (
   return NetLibDefaultUnload (ImageHandle);\r
 }\r
 \r
   return NetLibDefaultUnload (ImageHandle);\r
 }\r
 \r
-//@MT: EFI_DRIVER_ENTRY_POINT (Ip4ConfigDriverEntryPoint)\r
 \r
 EFI_STATUS\r
 Ip4ConfigDriverEntryPoint (\r
 \r
 EFI_STATUS\r
 Ip4ConfigDriverEntryPoint (\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c
new file mode 100644 (file)
index 0000000..7288875
--- /dev/null
@@ -0,0 +1,166 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+Module Name:\r
+\r
+  ComponentName.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+EFI_COMPONENT_NAME_PROTOCOL     gIp4ComponentName = {\r
+  Ip4ComponentNameGetDriverName,\r
+  Ip4ComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+static EFI_UNICODE_STRING_TABLE mIp4DriverNameTable[] = {\r
+  {\r
+    "eng",\r
+    L"IP4 Network Service Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable\r
+    name of the EFI Driver.\r
+\r
+  Arguments:\r
+    This       - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    Language   - A pointer to a three character ISO 639-2 language\r
+                 identifier. This is the language of the driver name\r
+                 that that the caller is requesting, and it must match\r
+                 one of the languages specified in SupportedLanguages.\r
+                 The number of languages supported by a driver is up to\r
+                 the driver writer.\r
+    DriverName - A pointer to the Unicode string to return.  This Unicode\r
+                 string is the name of the driver specified by This in the\r
+                 language specified by Language.\r
+\r
+  Returns:\r
+    EFI_SUCCES            - The Unicode string for the Driver specified by This\r
+                            and the language specified by Language was returned\r
+                            in DriverName.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - DriverName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return LookupUnicodeString (\r
+          Language,\r
+          gIp4ComponentName.SupportedLanguages,\r
+          mIp4DriverNameTable,\r
+          DriverName\r
+          );\r
+\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of\r
+    the controller that is being managed by an EFI Driver.\r
+\r
+  Arguments:\r
+    This             - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    ControllerHandle - The handle of a controller that the driver specified by\r
+                       This is managing.  This handle specifies the controller\r
+                       whose name is to be returned.\r
+    ChildHandle      - The handle of the child controller to retrieve the name\r
+                       of.  This is an optional parameter that may be NULL.  It\r
+                       will be NULL for device drivers.  It will also be NULL\r
+                       for a bus drivers that wish to retrieve the name of the\r
+                       bus controller.  It will not be NULL for a bus driver\r
+                       that wishes to retrieve the name of a child controller.\r
+    Language         - A pointer to a three character ISO 639-2 language\r
+                       identifier.  This is the language of the controller name\r
+                       that that the caller is requesting, and it must match one\r
+                       of the languages specified in SupportedLanguages.  The\r
+                       number of languages supported by a driver is up to the\r
+                       driver writer.\r
+    ControllerName   - A pointer to the Unicode string to return.  This Unicode\r
+                       string is the name of the controller specified by\r
+                       ControllerHandle and ChildHandle in the language\r
+                       specified by Language from the point of view of the\r
+                       driver specified by This.\r
+\r
+  Returns:\r
+    EFI_SUCCESS           - The Unicode string for the user readable name in the\r
+                            language specified by Language for the driver\r
+                            specified by This was returned in DriverName.\r
+    EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not\r
+                            a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - ControllerName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This is not currently\r
+                            managing the controller specified by\r
+                            ControllerHandle and ChildHandle.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c
new file mode 100644 (file)
index 0000000..ac339be
--- /dev/null
@@ -0,0 +1,421 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+Module Name:\r
+\r
+  Ip4Common.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+\r
+/**\r
+  Return the cast type (Unicast/Boradcast) specific to a\r
+  interface. All the addresses are host byte ordered.\r
+\r
+  @param  IpAddr                The IP address to classify in host byte order\r
+  @param  IpIf                  The interface that IpAddr received from\r
+\r
+  @return The cast type of this IP address specific to the interface.\r
+  @retval IP4_LOCAL_HOST        The IpAddr equals to the interface's address\r
+  @retval IP4_SUBNET_BROADCAST  The IpAddr is a directed subnet boradcast to  the\r
+                                interface\r
+  @retval IP4_NET_BROADCAST     The IpAddr is a network broadcast to the interface\r
+\r
+**/\r
+INTN\r
+Ip4GetNetCast (\r
+  IN  IP4_ADDR          IpAddr,\r
+  IN  IP4_INTERFACE     *IpIf\r
+  )\r
+{\r
+  if (IpAddr == IpIf->Ip) {\r
+    return IP4_LOCAL_HOST;\r
+\r
+  } else if (IpAddr == IpIf->SubnetBrdcast) {\r
+    return IP4_SUBNET_BROADCAST;\r
+\r
+  } else if (IpAddr == IpIf->NetBrdcast) {\r
+    return IP4_NET_BROADCAST;\r
+\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+\r
+/**\r
+  Find the cast type of the packet related to the local host.\r
+  This isn't the same as link layer cast type. For example, DHCP\r
+  server may send local broadcast to the local unicast MAC.\r
+\r
+  @param  IpSb                  The IP4 service binding instance that received the\r
+                                packet\r
+  @param  Dst                   The destination address in the packet (host byte\r
+                                order)\r
+  @param  Src                   The source address in the packet (host byte order)\r
+\r
+  @return The cast type for the Dst, it will return on the first non-promiscuous\r
+  @return cast type to a configured interface. If the packet doesn't match any of\r
+  @return the interface, multicast address and local broadcast address are checked.\r
+\r
+**/\r
+INTN\r
+Ip4GetHostCast (\r
+  IN  IP4_SERVICE       *IpSb,\r
+  IN  IP4_ADDR          Dst,\r
+  IN  IP4_ADDR          Src\r
+  )\r
+{\r
+  NET_LIST_ENTRY        *Entry;\r
+  IP4_INTERFACE         *IpIf;\r
+  INTN                  Type;\r
+  INTN                  Class;\r
+\r
+  Type = 0;\r
+\r
+  if (IpSb->MnpConfigData.EnablePromiscuousReceive) {\r
+    Type = IP4_PROMISCUOUS;\r
+  }\r
+\r
+  //\r
+  // Go through the interface list of the IP service, most likely.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
+\r
+    //\r
+    // Skip the unconfigured interface and invalid source address:\r
+    // source address can't be broadcast.\r
+    //\r
+    if (!IpIf->Configured || IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {\r
+      continue;\r
+    }\r
+\r
+    if ((Class = Ip4GetNetCast (Dst, IpIf)) > Type) {\r
+      return Class;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If it is local broadcast address. The source address must\r
+  // be a unicast address on one of the direct connected network.\r
+  // If it is a multicast address, accept it only if we are in\r
+  // the group.\r
+  //\r
+  if (Dst == IP4_ALLONE_ADDRESS) {\r
+    IpIf = Ip4FindNet (IpSb, Src);\r
+\r
+    if (IpIf && !IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {\r
+      return IP4_LOCAL_BROADCAST;\r
+    }\r
+\r
+  } else if (IP4_IS_MULTICAST (Dst) && Ip4FindGroup (&IpSb->IgmpCtrl, Dst)) {\r
+    return IP4_MULTICAST;\r
+  }\r
+\r
+  return Type;\r
+}\r
+\r
+\r
+/**\r
+  Find an interface whose configured IP address is Ip\r
+\r
+  @param  IpSb                  The IP4 service binding instance\r
+  @param  Ip                    The Ip address (host byte order) to find\r
+\r
+  @return The IP4_INTERFACE point if found, otherwise NULL\r
+\r
+**/\r
+IP4_INTERFACE *\r
+Ip4FindInterface (\r
+  IN IP4_SERVICE        *IpSb,\r
+  IN IP4_ADDR           Ip\r
+  )\r
+{\r
+  NET_LIST_ENTRY        *Entry;\r
+  IP4_INTERFACE         *IpIf;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
+\r
+    if (IpIf->Configured && (IpIf->Ip == Ip)) {\r
+      return IpIf;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Find an interface that Ip is on that connected network.\r
+\r
+  @param  IpSb                  The IP4 service binding instance\r
+  @param  Ip                    The Ip address (host byte order) to find\r
+\r
+  @return The IP4_INTERFACE point if found, otherwise NULL\r
+\r
+**/\r
+IP4_INTERFACE *\r
+Ip4FindNet (\r
+  IN IP4_SERVICE        *IpSb,\r
+  IN IP4_ADDR           Ip\r
+  )\r
+{\r
+  NET_LIST_ENTRY        *Entry;\r
+  IP4_INTERFACE         *IpIf;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
+\r
+    if (IpIf->Configured && IP4_NET_EQUAL (Ip, IpIf->Ip, IpIf->SubnetMask)) {\r
+      return IpIf;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Find an interface of the service with the same Ip/Netmask pair.\r
+\r
+  @param  IpSb                  Ip4 service binding instance\r
+  @param  Ip                    The Ip adress to find (host byte order)\r
+  @param  Netmask               The network to find (host byte order)\r
+\r
+  @return The IP4_INTERFACE point if found, otherwise NULL\r
+\r
+**/\r
+IP4_INTERFACE *\r
+Ip4FindStationAddress (\r
+  IN IP4_SERVICE        *IpSb,\r
+  IN IP4_ADDR           Ip,\r
+  IN IP4_ADDR           Netmask\r
+  )\r
+{\r
+  NET_LIST_ENTRY  *Entry;\r
+  IP4_INTERFACE   *IpIf;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
+\r
+    if (IpIf->Configured && (IpIf->Ip == Ip) && (IpIf->SubnetMask == Netmask)) {\r
+      return IpIf;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Get the MAC address for a multicast IP address. Call\r
+  Mnp's McastIpToMac to find the MAC address in stead of\r
+  hard code the NIC to be Ethernet.\r
+\r
+  @param  Mnp                   The Mnp instance to get the MAC address.\r
+  @param  Multicast             The multicast IP address to translate.\r
+  @param  Mac                   The buffer to hold the translated address.\r
+\r
+  @return Returns EFI_SUCCESS if the multicast IP is successfully\r
+  @return translated to a multicast MAC address. Otherwise some error.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4GetMulticastMac (\r
+  IN  EFI_MANAGED_NETWORK_PROTOCOL *Mnp,\r
+  IN  IP4_ADDR                     Multicast,\r
+  OUT EFI_MAC_ADDRESS              *Mac\r
+  )\r
+{\r
+  EFI_IP_ADDRESS        EfiIp;\r
+\r
+  EFI_IP4 (EfiIp.v4) = HTONL (Multicast);\r
+  return Mnp->McastIpToMac (Mnp, FALSE, &EfiIp, Mac);\r
+}\r
+\r
+\r
+/**\r
+  Convert the multibyte field in IP header's byter order.\r
+  In spite of its name, it can also be used to convert from\r
+  host to network byte order.\r
+\r
+  @param  Head                  The IP head to convert\r
+\r
+  @return Point to the converted IP head\r
+\r
+**/\r
+IP4_HEAD *\r
+Ip4NtohHead (\r
+  IN IP4_HEAD           *Head\r
+  )\r
+{\r
+  Head->TotalLen  = NTOHS (Head->TotalLen);\r
+  Head->Id        = NTOHS (Head->Id);\r
+  Head->Fragment  = NTOHS (Head->Fragment);\r
+  Head->Src       = NTOHL (Head->Src);\r
+  Head->Dst       = NTOHL (Head->Dst);\r
+\r
+  return Head;\r
+}\r
+\r
+\r
+/**\r
+  Set the Ip4 variable data.\r
+\r
+  @param  IpSb                  Ip4 service binding instance\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.\r
+  @retval other                 Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4SetVariableData (\r
+  IN IP4_SERVICE  *IpSb\r
+  )\r
+{\r
+  UINT32                 NumConfiguredInstance;\r
+  NET_LIST_ENTRY         *Entry;\r
+  UINTN                  VariableDataSize;\r
+  EFI_IP4_VARIABLE_DATA  *Ip4VariableData;\r
+  EFI_IP4_ADDRESS_PAIR   *Ip4AddressPair;\r
+  IP4_PROTOCOL           *IpInstance;\r
+  CHAR16                 *NewMacString;\r
+  EFI_STATUS             Status;\r
+\r
+  NumConfiguredInstance = 0;\r
+\r
+  //\r
+  // Go through the children list to count the configured children.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+    IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, Link, IP4_PROTOCOL_SIGNATURE);\r
+\r
+    if (IpInstance->State == IP4_STATE_CONFIGED) {\r
+      NumConfiguredInstance++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Calculate the size of the Ip4VariableData. As there may be no IP child,\r
+  // we should add extra buffer for the address paris only if the number of configured\r
+  // children is more than 1.\r
+  //\r
+  VariableDataSize = sizeof (EFI_IP4_VARIABLE_DATA);\r
+\r
+  if (NumConfiguredInstance > 1) {\r
+    VariableDataSize += sizeof (EFI_IP4_ADDRESS_PAIR) * (NumConfiguredInstance - 1);\r
+  }\r
+\r
+  Ip4VariableData = NetAllocatePool (VariableDataSize);\r
+  if (Ip4VariableData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ip4VariableData->DriverHandle = IpSb->Image;\r
+  Ip4VariableData->AddressCount = NumConfiguredInstance;\r
+\r
+  Ip4AddressPair = &Ip4VariableData->AddressPairs[0];\r
+\r
+  //\r
+  // Go through the children list to fill the configured children's address pairs.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+    IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, Link, IP4_PROTOCOL_SIGNATURE);\r
+\r
+    if (IpInstance->State == IP4_STATE_CONFIGED) {\r
+      Ip4AddressPair->InstanceHandle       = IpInstance->Handle;\r
+      EFI_IP4 (Ip4AddressPair->Ip4Address) = NTOHL (IpInstance->Interface->Ip);\r
+      EFI_IP4 (Ip4AddressPair->SubnetMask) = NTOHL (IpInstance->Interface->SubnetMask);\r
+\r
+      Ip4AddressPair++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Get the mac string.\r
+  //\r
+  Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (IpSb->MacString != NULL) {\r
+    //\r
+    // The variable is set already, we're going to update it.\r
+    //\r
+    if (StrCmp (IpSb->MacString, NewMacString) != 0) {\r
+      //\r
+      // The mac address is changed, delete the previous variable first.\r
+      //\r
+      gRT->SetVariable (\r
+             IpSb->MacString,\r
+             &gEfiIp4ServiceBindingProtocolGuid,\r
+             EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+             0,\r
+             NULL\r
+             );\r
+    }\r
+\r
+    NetFreePool (IpSb->MacString);\r
+  }\r
+\r
+  IpSb->MacString = NewMacString;\r
+\r
+  Status = gRT->SetVariable (\r
+                  IpSb->MacString,\r
+                  &gEfiIp4ServiceBindingProtocolGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                  VariableDataSize,\r
+                  (VOID *) Ip4VariableData\r
+                  );\r
+\r
+ON_ERROR:\r
+\r
+  NetFreePool (Ip4VariableData);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Clear the variable and free the resource.\r
+\r
+  @param  IpSb                  Ip4 service binding instance\r
+\r
+  @return None.\r
+\r
+**/\r
+VOID\r
+Ip4ClearVariableData (\r
+  IN IP4_SERVICE  *IpSb\r
+  )\r
+{\r
+  ASSERT (IpSb->MacString != NULL);\r
+\r
+  gRT->SetVariable (\r
+         IpSb->MacString,\r
+         &gEfiIp4ServiceBindingProtocolGuid,\r
+         EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+         0,\r
+         NULL\r
+         );\r
+\r
+  NetFreePool (IpSb->MacString);\r
+  IpSb->MacString = NULL;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h
new file mode 100644 (file)
index 0000000..59e5c11
--- /dev/null
@@ -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 (file)
index 0000000..eec6d69
--- /dev/null
@@ -0,0 +1,932 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Ip4Driver.c\r
+\r
+Abstract:\r
+\r
+  The driver binding and service binding protocol for IP4 driver.\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding = {\r
+  Ip4DriverBindingSupported,\r
+  Ip4DriverBindingStart,\r
+  Ip4DriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+//@MT: EFI_DRIVER_ENTRY_POINT (Ip4DriverEntryPoint)\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4DriverEntryPoint (\r
+  IN EFI_HANDLE             ImageHandle,\r
+  IN EFI_SYSTEM_TABLE       *SystemTable\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  The entry point for IP4 driver which install the driver\r
+  binding and component name protocol on its image.\r
+\r
+Arguments:\r
+\r
+  ImageHandle - The image handle of the driver\r
+  SystemTable - The system table\r
+\r
+Returns:\r
+\r
+  EFI_SUCCESS if the driver binding and component name protocols\r
+  are successfully installed, otherwise if failed.\r
+\r
+--*/\r
+{\r
+  return NetLibInstallAllDriverProtocols (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gIp4DriverBinding,\r
+           ImageHandle,\r
+           &gIp4ComponentName,\r
+           NULL,\r
+           NULL\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to test\r
+  @param  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                 device to start.\r
+\r
+  @retval EFI_SUCCES             This driver supports this device\r
+  @retval EFI_ALREADY_STARTED    This driver is already running on this device\r
+  @retval other                  This driver does not support this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  * This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     * RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Test for the MNP service binding Protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Test for the Arp service binding Protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiArpServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+Ip4CleanService (\r
+  IN IP4_SERVICE            *IpSb\r
+  );\r
+\r
+\r
+/**\r
+  Create a new IP4 driver service binding protocol\r
+\r
+  @param  Controller             The controller that has MNP service binding\r
+                                 installed\r
+  @param  ImageHandle            The IP4 driver's image handle\r
+  @param  Service                The variable to receive the newly created IP4\r
+                                 service.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resource\r
+  @retval EFI_SUCCESS            A new IP4 service binding private is created.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Ip4CreateService (\r
+  IN  EFI_HANDLE            Controller,\r
+  IN  EFI_HANDLE            ImageHandle,\r
+  OUT IP4_SERVICE           **Service\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT (Service != NULL);\r
+\r
+  *Service = NULL;\r
+\r
+  //\r
+  // allocate a service private data then initialize all the filed to\r
+  // empty resources, so if any thing goes wrong when allocating\r
+  // resources, Ip4CleanService can be called to clean it up.\r
+  //\r
+  IpSb = NetAllocatePool (sizeof (IP4_SERVICE));\r
+\r
+  if (IpSb == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  IpSb->Signature                   = IP4_SERVICE_SIGNATURE;\r
+  IpSb->ServiceBinding.CreateChild  = Ip4ServiceBindingCreateChild;\r
+  IpSb->ServiceBinding.DestroyChild = Ip4ServiceBindingDestroyChild;\r
+  IpSb->State                       = IP4_SERVICE_UNSTARTED;\r
+  IpSb->InDestory                   = FALSE;\r
+\r
+  IpSb->NumChildren                 = 0;\r
+  NetListInit (&IpSb->Children);\r
+\r
+  NetListInit (&IpSb->Interfaces);\r
+  IpSb->DefaultInterface            = NULL;\r
+  IpSb->DefaultRouteTable           = NULL;\r
+\r
+  Ip4InitAssembleTable (&IpSb->Assemble);\r
+\r
+  IpSb->IgmpCtrl.Igmpv1QuerySeen    = 0;\r
+  NetListInit (&IpSb->IgmpCtrl.Groups);\r
+\r
+  IpSb->Image                       = ImageHandle;\r
+  IpSb->Controller                  = Controller;\r
+\r
+  IpSb->MnpChildHandle              = NULL;\r
+  IpSb->Mnp                         = NULL;\r
+\r
+  IpSb->MnpConfigData.ReceivedQueueTimeoutValue = 0;\r
+  IpSb->MnpConfigData.TransmitQueueTimeoutValue = 0;\r
+  IpSb->MnpConfigData.ProtocolTypeFilter        = IP4_ETHER_PROTO;\r
+  IpSb->MnpConfigData.EnableUnicastReceive      = TRUE;\r
+  IpSb->MnpConfigData.EnableMulticastReceive    = TRUE;\r
+  IpSb->MnpConfigData.EnableBroadcastReceive    = TRUE;\r
+  IpSb->MnpConfigData.EnablePromiscuousReceive  = FALSE;\r
+  IpSb->MnpConfigData.FlushQueuesOnReset        = TRUE;\r
+  IpSb->MnpConfigData.EnableReceiveTimestamps   = FALSE;\r
+  IpSb->MnpConfigData.DisableBackgroundPolling  = FALSE;\r
+\r
+  NetZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));\r
+\r
+  IpSb->Timer                       = NULL;\r
+  IpSb->Ip4Config                   = NULL;\r
+  IpSb->DoneEvent                   = NULL;\r
+  IpSb->ReconfigEvent               = NULL;\r
+\r
+  //\r
+  // Create various resources. First create the route table, timer\r
+  // event and MNP child. IGMP, interface's initialization depend\r
+  // on the MNP child.\r
+  //\r
+  IpSb->DefaultRouteTable = Ip4CreateRouteTable ();\r
+\r
+  if (IpSb->DefaultRouteTable == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  Ip4TimerTicking,\r
+                  IpSb,\r
+                  &IpSb->Timer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = NetLibCreateServiceChild (\r
+             Controller,\r
+             ImageHandle,\r
+             &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+             &IpSb->MnpChildHandle\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  IpSb->MnpChildHandle,\r
+                  &gEfiManagedNetworkProtocolGuid,\r
+                  (VOID **) &IpSb->Mnp,\r
+                  ImageHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip4ServiceConfigMnp (IpSb, TRUE);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip4InitIgmp (IpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  IpSb->DefaultInterface = Ip4CreateInterface (IpSb->Mnp, Controller, ImageHandle);\r
+\r
+  if (IpSb->DefaultInterface == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NetListInsertHead (&IpSb->Interfaces, &IpSb->DefaultInterface->Link);\r
+\r
+  IpSb->MacString = NULL;\r
+\r
+  *Service = IpSb;\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  Ip4CleanService (IpSb);\r
+  NetFreePool (IpSb);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Clean up a IP4 service binding instance. It will release all\r
+  the resource allocated by the instance. The instance may be\r
+  partly initialized, or partly destoried. If a resource is\r
+  destoried, it is marked as that in case the destory failed and\r
+  being called again later.\r
+\r
+  @param  IpSb                   The IP4 serviceing binding instance to clean up\r
+\r
+  @retval EFI_SUCCESS            The resource used by the instance are cleaned up\r
+  @retval Others                 Failed to clean up some of the resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4CleanService (\r
+  IN IP4_SERVICE            *IpSb\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  if (IpSb->DefaultInterface != NULL) {\r
+    Status = Ip4FreeInterface (IpSb->DefaultInterface, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    IpSb->DefaultInterface = NULL;\r
+  }\r
+\r
+  if (IpSb->DefaultRouteTable != NULL) {\r
+    Ip4FreeRouteTable (IpSb->DefaultRouteTable);\r
+    IpSb->DefaultRouteTable = NULL;\r
+  }\r
+\r
+  Ip4CleanAssembleTable (&IpSb->Assemble);\r
+\r
+  if (IpSb->MnpChildHandle != NULL) {\r
+    if (IpSb->Mnp) {\r
+      gBS->CloseProtocol (\r
+            IpSb->MnpChildHandle,\r
+            &gEfiManagedNetworkProtocolGuid,\r
+            IpSb->Image,\r
+            IpSb->Controller\r
+            );\r
+\r
+      IpSb->Mnp = NULL;\r
+    }\r
+\r
+    NetLibDestroyServiceChild (\r
+      IpSb->Controller,\r
+      IpSb->Image,\r
+      &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+      IpSb->MnpChildHandle\r
+      );\r
+\r
+    IpSb->MnpChildHandle = NULL;\r
+  }\r
+\r
+  if (IpSb->Timer != NULL) {\r
+    gBS->SetTimer (IpSb->Timer, TimerCancel, 0);\r
+    gBS->CloseEvent (IpSb->Timer);\r
+\r
+    IpSb->Timer = NULL;\r
+  }\r
+\r
+  if (IpSb->Ip4Config != NULL) {\r
+    IpSb->Ip4Config->Stop (IpSb->Ip4Config);\r
+\r
+    gBS->CloseProtocol (\r
+          IpSb->Controller,\r
+          &gEfiIp4ConfigProtocolGuid,\r
+          IpSb->Image,\r
+          IpSb->Controller\r
+          );\r
+\r
+    gBS->CloseEvent (IpSb->DoneEvent);\r
+    gBS->CloseEvent (IpSb->ReconfigEvent);\r
+    IpSb->Ip4Config = NULL;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to bind driver to\r
+  @param  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                 device to start.\r
+\r
+  @retval EFI_SUCCES             This driver is added to ControllerHandle\r
+  @retval EFI_ALREADY_STARTED    This driver is already running on ControllerHandle\r
+  @retval other                  This driver does not support this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  * This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     * RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Test for the Ip4 service binding protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiIp4ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  Status = Ip4CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Install the Ip4ServiceBinding Protocol onto ControlerHandle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &ControllerHandle,\r
+                  &gEfiIp4ServiceBindingProtocolGuid,\r
+                  &IpSb->ServiceBinding,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto FREE_SERVICE;\r
+  }\r
+\r
+  //\r
+  // ready to go: start the receiving and timer\r
+  //\r
+  Status = Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto UNINSTALL_PROTOCOL;\r
+  }\r
+\r
+  Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto UNINSTALL_PROTOCOL;\r
+  }\r
+\r
+  //\r
+  // Initialize the IP4 ID\r
+  //\r
+  mIp4Id = (UINT16)NET_RANDOM (NetRandomInitSeed ());\r
+\r
+  Ip4SetVariableData (IpSb);\r
+\r
+  return Status;\r
+\r
+UNINSTALL_PROTOCOL:\r
+  gBS->UninstallProtocolInterface (\r
+         ControllerHandle,\r
+         &gEfiIp4ServiceBindingProtocolGuid,\r
+         &IpSb->ServiceBinding\r
+         );\r
+\r
+FREE_SERVICE:\r
+  Ip4CleanService (IpSb);\r
+  NetFreePool (IpSb);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ControllerHandle       Handle of device to stop driver on\r
+  @param  NumberOfChildren       Number of Handles in ChildHandleBuffer. If  number\r
+                                 of children is zero stop the entire  bus driver.\r
+  @param  ChildHandleBuffer      List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCES             This driver is removed ControllerHandle\r
+  @retval other                  This driver was not removed from this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  IP4_SERVICE                   *IpSb;\r
+  IP4_PROTOCOL                  *IpInstance;\r
+  EFI_HANDLE                    NicHandle;\r
+  EFI_STATUS                    Status;\r
+  EFI_TPL                       OldTpl;\r
+  INTN                          State;\r
+\r
+  //\r
+  // IP4 driver opens the MNP child, ARP children or the IP4_CONFIG protocol\r
+  // by driver. So the ControllerHandle may be the MNP child handle, ARP child\r
+  // handle, or the NIC (UNDI) handle because IP4_CONFIG protocol is installed\r
+  // in the NIC handle.\r
+  //\r
+  //\r
+  // First, check whether it is the IP4_CONFIG protocol being uninstalled.\r
+  // IP4_CONFIG protocol is installed on the NIC handle. It isn't necessary\r
+  // to clean up the default configuration if IP4_CONFIG is being stopped.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiIp4ConfigProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    //\r
+    // Retrieve the IP4 service binding protocol. If failed, it is\r
+    // likely that Ip4 ServiceBinding is uninstalled already. In this\r
+    // case, return immediately.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    ControllerHandle,\r
+                    &gEfiIp4ServiceBindingProtocolGuid,\r
+                    (VOID **) &ServiceBinding,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    IpSb = IP4_SERVICE_FROM_PROTOCOL (ServiceBinding);\r
+\r
+    OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+    if (IpSb->Ip4Config && (IpSb->State != IP4_SERVICE_DESTORY)) {\r
+\r
+      IpSb->Ip4Config->Stop (IpSb->Ip4Config);\r
+\r
+      Status = gBS->CloseProtocol (\r
+                      ControllerHandle,\r
+                      &gEfiIp4ConfigProtocolGuid,\r
+                      IpSb->Image,\r
+                      ControllerHandle\r
+                      );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        NET_RESTORE_TPL (OldTpl);\r
+        return Status;\r
+      }\r
+\r
+      //\r
+      // If the auto configure hasn't complete, mark it as not started.\r
+      //\r
+      if (IpSb->State == IP4_SERVICE_STARTED) {\r
+        IpSb->State = IP4_SERVICE_UNSTARTED;\r
+      }\r
+\r
+      IpSb->Ip4Config = NULL;\r
+      gBS->CloseEvent (IpSb->DoneEvent);\r
+      gBS->CloseEvent (IpSb->ReconfigEvent);\r
+    }\r
+\r
+    NET_RESTORE_TPL (OldTpl);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Either MNP or ARP protocol is being uninstalled. The controller\r
+  // handle is either the MNP child or ARP child. But, the IP4's\r
+  // service binding is installed on the NIC handle. So, need to open\r
+  // the protocol info to find the NIC handle.\r
+  //\r
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);\r
+\r
+  if (NicHandle == NULL) {\r
+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);\r
+  }\r
+\r
+  if (NicHandle == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Retrieve the IP4 service binding protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  &gEfiIp4ServiceBindingProtocolGuid,\r
+                  (VOID **) &ServiceBinding,\r
+                  This->DriverBindingHandle,\r
+                  NicHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  IpSb   = IP4_SERVICE_FROM_PROTOCOL (ServiceBinding);\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  if (IpSb->InDestory) {\r
+    NET_RESTORE_TPL (OldTpl);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  IpSb->InDestory = TRUE;\r
+\r
+  State           = IpSb->State;\r
+  IpSb->State     = IP4_SERVICE_DESTORY;\r
+\r
+  //\r
+  // Destory all the children first. If not all children are destoried,\r
+  // the IP driver can operate correctly, so restore it state. Don't\r
+  // use NET_LIST_FOR_EACH_SAFE here, because it will cache the next\r
+  // pointer, which may point to the child that has already been destoried.\r
+  // For example, if there are two child in the list, the first is UDP\r
+  // listen child, the send is the MTFTP's child. When Udp child is\r
+  // destoried, it will destory the MTFTP's child. Then Next point to\r
+  // a invalid child.\r
+  //\r
+  while (!NetListIsEmpty (&IpSb->Children)) {\r
+    IpInstance = NET_LIST_HEAD (&IpSb->Children, IP4_PROTOCOL, Link);\r
+    Ip4ServiceBindingDestroyChild (ServiceBinding, IpInstance->Handle);\r
+  }\r
+\r
+  if (IpSb->NumChildren != 0) {\r
+    IpSb->State = State;\r
+    Status      = EFI_DEVICE_ERROR;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Clear the variable data.\r
+  //\r
+  Ip4ClearVariableData (IpSb);\r
+\r
+  //\r
+  // OK, clean other resources then uninstall the service binding protocol.\r
+  //\r
+  Status = Ip4CleanService (IpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  NicHandle,\r
+                  &gEfiIp4ServiceBindingProtocolGuid,\r
+                  ServiceBinding\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+  NetFreePool (IpSb);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  IpSb->InDestory = FALSE;\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Creates a child handle with a set of I/O services.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ChildHandle            Pointer to the handle of the child to create.   If\r
+                                 it is NULL, then a new handle is created.   If it\r
+                                 is not NULL, then the I/O services are  added to\r
+                                 the existing child handle.\r
+\r
+  @retval EFI_SUCCES             The child handle was created with the I/O services\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources availabe to create\r
+                                 the child\r
+  @retval other                  The child handle was not created\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4ServiceBindingCreateChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    *ChildHandle\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+  IP4_PROTOCOL              *IpInstance;\r
+  EFI_TPL                   OldTpl;\r
+  EFI_STATUS                Status;\r
+  VOID                      *Mnp;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpSb       = IP4_SERVICE_FROM_PROTOCOL (This);\r
+  IpInstance = NetAllocatePool (sizeof (IP4_PROTOCOL));\r
+\r
+  if (IpInstance == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ip4InitProtocol (IpSb, IpInstance);\r
+\r
+  //\r
+  // Install Ip4 onto ChildHandle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiIp4ProtocolGuid,\r
+                  &IpInstance->Ip4Proto,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  IpInstance->Handle = *ChildHandle;\r
+\r
+  //\r
+  // Open the Managed Network protocol BY_CHILD.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  IpSb->MnpChildHandle,\r
+                  &gEfiManagedNetworkProtocolGuid,\r
+                  (VOID **) &Mnp,\r
+                  gIp4DriverBinding.DriverBindingHandle,\r
+                  IpInstance->Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           ChildHandle,\r
+           &gEfiIp4ProtocolGuid,\r
+           &IpInstance->Ip4Proto,\r
+           NULL\r
+           );\r
+\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Insert it into the service binding instance.\r
+  //\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  NetListInsertTail (&IpSb->Children, &IpInstance->Link);\r
+  IpSb->NumChildren++;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+ON_ERROR:\r
+\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    Ip4CleanProtocol (IpInstance);\r
+\r
+    NetFreePool (IpInstance);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Destroys a child handle with a set of I/O services.\r
+\r
+  @param  This                   Protocol instance pointer.\r
+  @param  ChildHandle            Handle of the child to destroy\r
+\r
+  @retval EFI_SUCCES             The I/O services were removed from the child\r
+                                 handle\r
+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services\r
+                                  that are being removed\r
+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.\r
+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because\r
+                                 its  I/O services are being used.\r
+  @retval other                  The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip4ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  IP4_SERVICE               *IpSb;\r
+  IP4_PROTOCOL              *IpInstance;\r
+  EFI_IP4_PROTOCOL          *Ip4;\r
+  EFI_TPL                   OldTpl;\r
+  INTN                      State;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Retrieve the private context data structures\r
+  //\r
+  IpSb   = IP4_SERVICE_FROM_PROTOCOL (This);\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiIp4ProtocolGuid,\r
+                  (VOID **) &Ip4,\r
+                  gIp4DriverBinding.DriverBindingHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (Ip4);\r
+\r
+  if (IpInstance->Service != IpSb) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  //\r
+  // A child can be destoried more than once. For example,\r
+  // Ip4DriverBindingStop will destory all of its children.\r
+  // when UDP driver is being stopped, it will destory all\r
+  // the IP child it opens.\r
+  //\r
+  if (IpInstance->State == IP4_STATE_DESTORY) {\r
+    NET_RESTORE_TPL (OldTpl);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  State             = IpInstance->State;\r
+  IpInstance->State = IP4_STATE_DESTORY;\r
+\r
+  //\r
+  // Close the Managed Network protocol.\r
+  //\r
+  gBS->CloseProtocol (\r
+         IpSb->MnpChildHandle,\r
+         &gEfiManagedNetworkProtocolGuid,\r
+         gIp4DriverBinding.DriverBindingHandle,\r
+         ChildHandle\r
+         );\r
+\r
+  //\r
+  // Uninstall the IP4 protocol first. Many thing happens during\r
+  // this:\r
+  // 1. The consumer of the IP4 protocol will be stopped if it\r
+  // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is\r
+  // stopped, IP driver's stop function will be called, and uninstall\r
+  // EFI_IP4_PROTOCOL will trigger the UDP's stop function. This\r
+  // makes it possible to create the network stack bottom up, and\r
+  // stop it top down.\r
+  // 2. the upper layer will recycle the received packet. The recycle\r
+  // event's TPL is higher than this function. The recycle events\r
+  // will be called back before preceeding. If any packets not recycled,\r
+  // that means there is a resource leak.\r
+  //\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  ChildHandle,\r
+                  &gEfiIp4ProtocolGuid,\r
+                  &IpInstance->Ip4Proto\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip4CleanProtocol (IpInstance);\r
+\r
+  Ip4SetVariableData (IpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->InstallMultipleProtocolInterfaces (\r
+           &ChildHandle,\r
+           &gEfiIp4ProtocolGuid,\r
+           Ip4,\r
+           NULL\r
+           );\r
+\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NetListRemoveEntry (&IpInstance->Link);\r
+  IpSb->NumChildren--;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  NetFreePool (IpInstance);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  IpInstance->State = State;\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h
new file mode 100644 (file)
index 0000000..5421c90
--- /dev/null
@@ -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 (file)
index 0000000..f45cc3f
--- /dev/null
@@ -0,0 +1,81 @@
+#/** @file\r
+# Component name for module Ip4\r
+#\r
+# Copyright (c) 2007, Intel Corporation\r
+#\r
+#  All rights reserved. This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution. The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = Ip4Dxe\r
+  FILE_GUID                      = 9FB1A1F3-3B71-4324-B39A-745CBB015FFF\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  EDK_RELEASE_VERSION            = 0x00020000\r
+  EFI_SPECIFICATION_VERSION      = 0x00020000\r
+\r
+  ENTRY_POINT                    = Ip4DriverEntryPoint\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources.common]\r
+  Ip4Driver.c\r
+  Ip4Option.h\r
+  Ip4Route.h\r
+  Ip4If.c\r
+  Ip4Igmp.h\r
+  Ip4Output.c\r
+  Ip4Icmp.c\r
+  Ip4Igmp.c\r
+  Ip4Impl.c\r
+  Ip4Common.h\r
+  Ip4Impl.h\r
+  Ip4Driver.h\r
+  Ip4Common.c\r
+  Ip4If.h\r
+  Ip4Option.c\r
+  Ip4Output.h\r
+  ComponentName.c\r
+  Ip4Input.h\r
+  Ip4Route.c\r
+  Ip4Icmp.h\r
+  Ip4Input.c\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  UefiLib\r
+  BaseLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  UefiRuntimeServicesTableLib\r
+  DebugLib\r
+  NetLib\r
+\r
+\r
+[Protocols]\r
+  gEfiIp4ProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiManagedNetworkServiceBindingProtocolGuid  # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIp4ConfigProtocolGuid                     # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiArpServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIp4ServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiManagedNetworkProtocolGuid                # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiArpProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED\r
+\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa
new file mode 100644 (file)
index 0000000..c2059dd
--- /dev/null
@@ -0,0 +1,101 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+  <MsaHeader>\r
+    <ModuleName>Ip4Dxe</ModuleName>\r
+    <ModuleType>DXE_DRIVER</ModuleType>\r
+    <GuidValue>9FB1A1F3-3B71-4324-B39A-745CBB015FFF</GuidValue>\r
+    <Version>1.0</Version>\r
+    <Abstract>Component name for module Ip4</Abstract>\r
+    <Description>FIX ME!</Description>\r
+    <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>\r
+    <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>\r
+    <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION   0x00000052</Specification>\r
+  </MsaHeader>\r
+  <ModuleDefinitions>\r
+    <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>\r
+    <BinaryModule>false</BinaryModule>\r
+    <OutputFileBasename>Ip4Dxe</OutputFileBasename>\r
+  </ModuleDefinitions>\r
+  <LibraryClassDefinitions>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>DebugLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiRuntimeServicesTableLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiDriverEntryPoint</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiBootServicesTableLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>BaseLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiLib</Keyword>\r
+    </LibraryClass>\r
+  </LibraryClassDefinitions>\r
+  <SourceFiles>\r
+    <Filename>Ip4Input.c</Filename>\r
+    <Filename>Ip4Icmp.h</Filename>\r
+    <Filename>Ip4Route.c</Filename>\r
+    <Filename>Ip4Input.h</Filename>\r
+    <Filename>ComponentName.c</Filename>\r
+    <Filename>Ip4Output.h</Filename>\r
+    <Filename>Ip4Option.c</Filename>\r
+    <Filename>Ip4If.h</Filename>\r
+    <Filename>Ip4Common.c</Filename>\r
+    <Filename>Ip4Driver.h</Filename>\r
+    <Filename>Ip4Impl.h</Filename>\r
+    <Filename>Ip4Common.h</Filename>\r
+    <Filename>Ip4Impl.c</Filename>\r
+    <Filename>Ip4Igmp.c</Filename>\r
+    <Filename>Ip4Icmp.c</Filename>\r
+    <Filename>Ip4Output.c</Filename>\r
+    <Filename>Ip4Igmp.h</Filename>\r
+    <Filename>Ip4If.c</Filename>\r
+    <Filename>Ip4Route.h</Filename>\r
+    <Filename>Ip4Option.h</Filename>\r
+    <Filename>Ip4Driver.c</Filename>\r
+  </SourceFiles>\r
+  <PackageDependencies>\r
+    <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>\r
+    <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>\r
+  </PackageDependencies>\r
+  <Protocols>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiArpProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiManagedNetworkProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiIp4ServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiArpServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiIp4ConfigProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiManagedNetworkServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiIp4ProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+  </Protocols>\r
+  <Externs>\r
+    <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>\r
+    <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>\r
+    <Extern>\r
+      <ModuleEntryPoint>Ip4DriverEntryPoint</ModuleEntryPoint>\r
+    </Extern>\r
+  </Externs>\r
+</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 (file)
index 0000000..7f29c90
--- /dev/null
@@ -0,0 +1,372 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+Module Name:\r
+\r
+  Ip4Icmp.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+IP4_ICMP_CLASS\r
+mIcmpClass[] = {\r
+  {ICMP_ECHO_REPLY,         ICMP_QUERY_MESSAGE  },\r
+  {1,                       ICMP_INVALID_MESSAGE},\r
+  {2,                       ICMP_INVALID_MESSAGE},\r
+  {ICMP_DEST_UNREACHABLE,   ICMP_ERROR_MESSAGE  },\r
+  {ICMP_SOURCE_QUENCH,      ICMP_ERROR_MESSAGE  },\r
+  {ICMP_REDIRECT,           ICMP_ERROR_MESSAGE  },\r
+  {6,                       ICMP_INVALID_MESSAGE},\r
+  {7,                       ICMP_INVALID_MESSAGE},\r
+  {ICMP_ECHO_REQUEST,       ICMP_QUERY_MESSAGE  },\r
+  {9,                       ICMP_INVALID_MESSAGE},\r
+  {10,                      ICMP_INVALID_MESSAGE},\r
+  {ICMP_TIME_EXCEEDED,      ICMP_ERROR_MESSAGE  },\r
+  {ICMP_PARAMETER_PROBLEM,  ICMP_ERROR_MESSAGE  },\r
+  {ICMP_TIMESTAMP ,         ICMP_QUERY_MESSAGE  },\r
+  {14,                      ICMP_INVALID_MESSAGE},\r
+  {ICMP_INFO_REQUEST ,      ICMP_QUERY_MESSAGE  },\r
+  {ICMP_INFO_REPLY ,        ICMP_QUERY_MESSAGE  },\r
+};\r
+\r
+EFI_IP4_ICMP_TYPE\r
+mIp4SupportedIcmp [23] = {\r
+  {ICMP_ECHO_REPLY,        ICMP_DEFAULT_CODE      },\r
+\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNREACHABLE   },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNREACHABLE  },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_PROTO_UNREACHABLE },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_PORT_UNREACHABLE  },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_FRAGMENT_FAILED   },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_SOURCEROUTE_FAILED},\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNKNOWN       },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNKNOWN      },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_SOURCE_ISOLATED   },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_NET_PROHIBITED    },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_HOST_PROHIBITED   },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNREACHABLE_TOS },\r
+  {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNREACHABLE_TOS},\r
+\r
+  {ICMP_SOURCE_QUENCH,     ICMP_DEFAULT_CODE      },\r
+\r
+  {ICMP_REDIRECT,          ICMP_NET_REDIRECT      },\r
+  {ICMP_REDIRECT,          ICMP_HOST_REDIRECT     },\r
+  {ICMP_REDIRECT,          ICMP_NET_TOS_REDIRECT  },\r
+  {ICMP_REDIRECT,          ICMP_HOST_TOS_REDIRECT },\r
+\r
+  {ICMP_ECHO_REQUEST,      ICMP_DEFAULT_CODE      },\r
+\r
+  {ICMP_TIME_EXCEEDED,     ICMP_TIMEOUT_IN_TRANSIT},\r
+  {ICMP_TIME_EXCEEDED,     ICMp_TIMEOUT_REASSEMBLE},\r
+\r
+  {ICMP_PARAMETER_PROBLEM, ICMP_DEFAULT_CODE      },\r
+};\r
+\r
+\r
+\r
+/**\r
+  Process the ICMP redirect. Find the instance then update\r
+  its route cache.\r
+  All kinds of redirect is treated as host redirect as\r
+  specified by RFC1122 3.3.1.2:\r
+  "Since the subnet mask appropriate to the destination\r
+  address is generally not known, a Network Redirect\r
+  message SHOULD be treated identically to a Host Redirect\r
+  message;"\r
+\r
+  @param  IpSb                   The IP4 service binding instance that received the\r
+                                 packet\r
+  @param  Head                   The IP head of the received ICMPpacket.\r
+  @param  Packet                 The content of the ICMP redirect packet with IP\r
+                                 head removed.\r
+  @param  Icmp                   The buffer to store the ICMP error message if\r
+                                 something is wrong.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid\r
+  @retval EFI_SUCCESS            Successfully updated the route caches\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Ip4ProcessIcmpRedirect (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet,\r
+  IN IP4_ICMP_ERROR_HEAD    *Icmp\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  IP4_PROTOCOL              *Ip4Instance;\r
+  IP4_ROUTE_CACHE_ENTRY     *CacheEntry;\r
+  IP4_INTERFACE             *IpIf;\r
+  IP4_ADDR                  Gateway;\r
+\r
+  //\r
+  // Find the interface whose IP address is the source of the\r
+  // orgianl IP packet.\r
+  //\r
+  IpIf    = Ip4FindInterface (IpSb, NTOHL (Icmp->IpHead.Src));\r
+  Gateway = NTOHL (Icmp->Fourth);\r
+\r
+  //\r
+  // discard the packet if the new gateway address it specifies\r
+  // is not on the same connected net through which the Redirect\r
+  // arrived. (RFC1122 3.2.2.2).\r
+  //\r
+  if ((IpIf == NULL) || !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask)) {\r
+    NetbufFree (Packet);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Update each IP child's route cache on the interface.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
+    Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);\r
+\r
+    if (Ip4Instance->RouteTable == NULL) {\r
+      continue;\r
+    }\r
+\r
+    CacheEntry = Ip4FindRouteCache (\r
+                   Ip4Instance->RouteTable,\r
+                   NTOHL (Icmp->IpHead.Dst),\r
+                   NTOHL (Icmp->IpHead.Src)\r
+                   );\r
+\r
+    //\r
+    // Only update the route cache's gateway if the source of the\r
+    // Redirect is the current first-hop gateway\r
+    //\r
+    if ((CacheEntry != NULL) && (NTOHL (Head->Src) == CacheEntry->NextHop)) {\r
+      CacheEntry->NextHop = Gateway;\r
+    }\r
+  }\r
+\r
+  NetbufFree (Packet);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Process the ICMP error packet. If it is an ICMP redirect packet,\r
+  update call Ip4ProcessIcmpRedirect to update the IP instance's\r
+  route cache, otherwise, deliver the packet to upper layer.\r
+\r
+  @param  IpSb                   The IP service that received the packet.\r
+  @param  Head                   The IP head of the ICMP error packet\r
+  @param  Packet                 The content of the ICMP error with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid\r
+  @retval Others                 Failed to process the packet.\r
+  @retval EFI_SUCCESS            The ICMP error is processed successfully.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Ip4ProcessIcmpError (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP4_ICMP_ERROR_HEAD       Icmp;\r
+\r
+  if (Packet->TotalSize < sizeof (Icmp)) {\r
+    NetbufFree (Packet);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+  //\r
+  // If it is an ICMP redirect error, update the route cache\r
+  // as RFC1122. Otherwise, demultiplex it to IP instances.\r
+  //\r
+  if (Icmp.Head.Type == ICMP_REDIRECT) {\r
+    return Ip4ProcessIcmpRedirect (IpSb, Head, Packet, &Icmp);\r
+  }\r
+\r
+  IP4_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;\r
+  return Ip4Demultiplex (IpSb, Head, Packet);\r
+}\r
+\r
+\r
+/**\r
+  Replay an ICMP echo request.\r
+\r
+  @param  IpSb                   The IP service that receivd the packet\r
+  @param  Head                   The IP head of the ICMP error packet\r
+  @param  Packet                 The content of the ICMP error with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource.\r
+  @retval EFI_SUCCESS            The ICMP Echo request is successfully answered.\r
+  @retval Others                 Failed to answer the ICMP echo request.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4IcmpReplyEcho (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP4_ICMP_QUERY_HEAD       *Icmp;\r
+  NET_BUF                   *Data;\r
+  EFI_STATUS                Status;\r
+  IP4_HEAD                  ReplyHead;\r
+\r
+  //\r
+  // make a copy the packet, it is really a bad idea to\r
+  // send the MNP's buffer back to MNP.\r
+  //\r
+  Data = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);\r
+\r
+  if (Data == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Change the ICMP type to echo reply, exchange the source\r
+  // and destination, then send it. The source is updated to\r
+  // use specific destination. See RFC1122. SRR/RR option\r
+  // update is omitted.\r
+  //\r
+  Icmp                = (IP4_ICMP_QUERY_HEAD *) NetbufGetByte (Data, 0, NULL);\r
+  Icmp->Head.Type     = ICMP_ECHO_REPLY;\r
+  Icmp->Head.Checksum = 0;\r
+  Icmp->Head.Checksum = ~NetblockChecksum ((UINT8 *) Icmp, Data->TotalSize);\r
+\r
+  ReplyHead.Tos       = 0;\r
+  ReplyHead.Fragment  = 0;\r
+  ReplyHead.Ttl       = 64;\r
+  ReplyHead.Protocol  = IP4_PROTO_ICMP;\r
+  ReplyHead.Src       = 0;\r
+\r
+  //\r
+  // Ip4Output will select a source for us\r
+  //\r
+  ReplyHead.Dst = Head->Src;\r
+\r
+  Status = Ip4Output (\r
+             IpSb,\r
+             NULL,\r
+             Data,\r
+             &ReplyHead,\r
+             NULL,\r
+             0,\r
+             IP4_ALLZERO_ADDRESS,\r
+             Ip4SysPacketSent,\r
+             NULL\r
+             );\r
+\r
+ON_EXIT:\r
+  NetbufFree (Packet);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Process the ICMP query message. If it is an ICMP echo\r
+  request, answer it. Otherwise deliver it to upper layer.\r
+\r
+  @param  IpSb                   The IP service that receivd the packet\r
+  @param  Head                   The IP head of the ICMP query packet\r
+  @param  Packet                 The content of the ICMP query with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid\r
+  @retval EFI_SUCCESS            The ICMP query message is processed\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4ProcessIcmpQuery (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP4_ICMP_QUERY_HEAD       Icmp;\r
+\r
+  if (Packet->TotalSize < sizeof (Icmp)) {\r
+    NetbufFree (Packet);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+  if (Icmp.Head.Type == ICMP_ECHO_REQUEST) {\r
+    return Ip4IcmpReplyEcho (IpSb, Head, Packet);\r
+  }\r
+\r
+  return Ip4Demultiplex (IpSb, Head, Packet);\r
+}\r
+\r
+\r
+/**\r
+  Handle the ICMP packet. First validate the message format,\r
+  then according to the message types, process it as query or\r
+  error packet.\r
+\r
+  @param  IpSb                   The IP service that receivd the packet\r
+  @param  Head                   The IP head of the ICMP query packet\r
+  @param  Packet                 The content of the ICMP query with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformated.\r
+  @retval EFI_SUCCESS            The ICMP message is successfully processed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4IcmpHandle (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP4_ICMP_HEAD             Icmp;\r
+  UINT16                    Checksum;\r
+\r
+  if (Packet->TotalSize < sizeof (Icmp)) {\r
+    goto DROP;\r
+  }\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+  if (Icmp.Type > ICMP_TYPE_MAX) {\r
+    goto DROP;\r
+  }\r
+\r
+  Checksum = ~NetbufChecksum (Packet);\r
+  if ((Icmp.Checksum != 0) && (Checksum != 0)) {\r
+    goto DROP;\r
+  }\r
+\r
+  if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_ERROR_MESSAGE) {\r
+    return Ip4ProcessIcmpError (IpSb, Head, Packet);\r
+\r
+  } else if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_QUERY_MESSAGE) {\r
+    return Ip4ProcessIcmpQuery (IpSb, Head, Packet);\r
+\r
+  }\r
+\r
+DROP:\r
+  NetbufFree (Packet);\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h
new file mode 100644 (file)
index 0000000..f4641e2
--- /dev/null
@@ -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 (file)
index 0000000..952eb98
--- /dev/null
@@ -0,0 +1,1146 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+Module Name:\r
+\r
+  Ip4If.c\r
+\r
+Abstract:\r
+\r
+  Implement IP4 pesudo interface.\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+//\r
+// Mac address with all zero, used to determine whethter the ARP\r
+// resolve succeeded. Failed ARP requests zero the MAC address buffer.\r
+//\r
+STATIC EFI_MAC_ADDRESS  mZeroMacAddress;\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Ip4OnFrameSent (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Ip4OnArpResolved (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Ip4OnFrameReceived (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+STATIC\r
+VOID\r
+Ip4CancelFrameArp (\r
+  IN IP4_ARP_QUE            *ArpQue,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN IP4_FRAME_TO_CANCEL    FrameToCancel, OPTIONAL\r
+  IN VOID                   *Context\r
+  );\r
+\r
+\r
+/**\r
+  Wrap a transmit request into a newly allocated IP4_LINK_TX_TOKEN.\r
+\r
+  @param  Interface             The interface to send out from\r
+  @param  IpInstance            The IpInstance that transmit the packet.  NULL if\r
+                                the packet is sent by the IP4 driver itself.\r
+  @param  Packet                The packet to transmit\r
+  @param  CallBack              Call back function to execute if transmission\r
+                                finished.\r
+  @param  Context               Opaque parameter to the call back.\r
+\r
+  @return The wrapped token if succeed or NULL\r
+\r
+**/\r
+STATIC\r
+IP4_LINK_TX_TOKEN *\r
+Ip4WrapLinkTxToken (\r
+  IN IP4_INTERFACE          *Interface,\r
+  IN IP4_PROTOCOL           *IpInstance,    OPTIONAL\r
+  IN NET_BUF                *Packet,\r
+  IN IP4_FRAME_CALLBACK     CallBack,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
+  EFI_MANAGED_NETWORK_TRANSMIT_DATA     *MnpTxData;\r
+  IP4_LINK_TX_TOKEN                     *Token;\r
+  EFI_STATUS                            Status;\r
+  UINT32                                Count;\r
+\r
+  Token = NetAllocatePool (sizeof (IP4_LINK_TX_TOKEN) + \\r
+            (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));\r
+\r
+  if (Token == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Token->Signature = IP4_FRAME_TX_SIGNATURE;\r
+  NetListInit (&Token->Link);\r
+\r
+  Token->Interface  = Interface;\r
+  Token->IpInstance = IpInstance;\r
+  Token->CallBack   = CallBack;\r
+  Token->Packet     = Packet;\r
+  Token->Context    = Context;\r
+  CopyMem (&Token->DstMac, &mZeroMacAddress, sizeof (EFI_MAC_ADDRESS));\r
+  CopyMem (&Token->SrcMac, &Interface->Mac, sizeof (EFI_MAC_ADDRESS));\r
+\r
+  MnpToken          = &(Token->MnpToken);\r
+  MnpToken->Status  = EFI_NOT_READY;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ip4OnFrameSent,\r
+                  Token,\r
+                  &MnpToken->Event\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (Token);\r
+    return NULL;\r
+  }\r
+\r
+  MnpTxData                     = &Token->MnpTxData;\r
+  MnpToken->Packet.TxData       = MnpTxData;\r
+\r
+  MnpTxData->DestinationAddress = &Token->DstMac;\r
+  MnpTxData->SourceAddress      = &Token->SrcMac;\r
+  MnpTxData->ProtocolType       = IP4_ETHER_PROTO;\r
+  MnpTxData->DataLength         = Packet->TotalSize;\r
+  MnpTxData->HeaderLength       = 0;\r
+\r
+  Count                         = Packet->BlockOpNum;\r
+\r
+  NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);\r
+  MnpTxData->FragmentCount      = (UINT16)Count;\r
+\r
+  return Token;\r
+}\r
+\r
+\r
+/**\r
+  Free the link layer transmit token. It will close the event\r
+  then free the memory used.\r
+\r
+  @param  Token                 Token to free\r
+\r
+  @return NONE\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4FreeLinkTxToken (\r
+  IN IP4_LINK_TX_TOKEN      *Token\r
+  )\r
+{\r
+  NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);\r
+\r
+  gBS->CloseEvent (Token->MnpToken.Event);\r
+  NetFreePool (Token);\r
+}\r
+\r
+\r
+/**\r
+  Create an IP_ARP_QUE structure to request ARP service.\r
+\r
+  @param  Interface             The interface to send ARP from.\r
+  @param  DestIp                The destination IP (host byte order) to request MAC\r
+                                for\r
+\r
+  @return Point to newly created IP4_ARP_QUE if succeed, otherwise NULL.\r
+\r
+**/\r
+STATIC\r
+IP4_ARP_QUE *\r
+Ip4CreateArpQue (\r
+  IN IP4_INTERFACE          *Interface,\r
+  IN IP4_ADDR               DestIp\r
+  )\r
+{\r
+  IP4_ARP_QUE               *ArpQue;\r
+  EFI_STATUS                Status;\r
+\r
+  ArpQue = NetAllocatePool (sizeof (IP4_ARP_QUE));\r
+\r
+  if (ArpQue == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  ArpQue->Signature = IP4_FRAME_ARP_SIGNATURE;\r
+  NetListInit (&ArpQue->Link);\r
+\r
+  NetListInit (&ArpQue->Frames);\r
+  ArpQue->Interface = Interface;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ip4OnArpResolved,\r
+                  ArpQue,\r
+                  &ArpQue->OnResolved\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (ArpQue);\r
+    return NULL;\r
+  }\r
+\r
+  ArpQue->Ip  = DestIp;\r
+  CopyMem (&ArpQue->Mac, &mZeroMacAddress, sizeof (EFI_MAC_ADDRESS));\r
+\r
+  return ArpQue;\r
+}\r
+\r
+\r
+/**\r
+  Remove all the transmit requests queued on the ARP queue, then free it.\r
+\r
+  @param  ArpQue                Arp queue to free\r
+  @param  IoStatus              The transmit status returned to transmit requests'\r
+                                callback.\r
+\r
+  @return NONE\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4FreeArpQue (\r
+  IN IP4_ARP_QUE            *ArpQue,\r
+  IN EFI_STATUS             IoStatus\r
+  )\r
+{\r
+  NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);\r
+\r
+  //\r
+  // Remove all the frame waiting the ARP response\r
+  //\r
+  Ip4CancelFrameArp (ArpQue, IoStatus, NULL, NULL);\r
+\r
+  gBS->CloseEvent (ArpQue->OnResolved);\r
+  NetFreePool (ArpQue);\r
+}\r
+\r
+\r
+/**\r
+  Create a link layer receive token to wrap the receive request\r
+\r
+  @param  Interface             The interface to receive from\r
+  @param  IpInstance            The instance that request the receive (NULL for IP4\r
+                                driver itself)\r
+  @param  CallBack              Call back function to execute when finished.\r
+  @param  Context               Opaque parameters to the callback\r
+\r
+  @return Point to created IP4_LINK_RX_TOKEN if succeed, otherwise NULL.\r
+\r
+**/\r
+STATIC\r
+IP4_LINK_RX_TOKEN *\r
+Ip4CreateLinkRxToken (\r
+  IN IP4_INTERFACE          *Interface,\r
+  IN IP4_PROTOCOL           *IpInstance,\r
+  IN IP4_FRAME_CALLBACK     CallBack,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
+  IP4_LINK_RX_TOKEN                     *Token;\r
+  EFI_STATUS                            Status;\r
+\r
+  Token = NetAllocatePool (sizeof (IP4_LINK_RX_TOKEN));\r
+  if (Token == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Token->Signature  = IP4_FRAME_RX_SIGNATURE;\r
+  Token->Interface  = Interface;\r
+  Token->IpInstance = IpInstance;\r
+  Token->CallBack   = CallBack;\r
+  Token->Context    = Context;\r
+\r
+  MnpToken          = &Token->MnpToken;\r
+  MnpToken->Status  = EFI_NOT_READY;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ip4OnFrameReceived,\r
+                  Token,\r
+                  &MnpToken->Event\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (Token);\r
+    return NULL;\r
+  }\r
+\r
+  MnpToken->Packet.RxData = NULL;\r
+  return Token;\r
+}\r
+\r
+\r
+/**\r
+  Free the link layer request token. It will close the event\r
+  then free the memory used.\r
+\r
+  @param  Token                 Request token to free\r
+\r
+  @return NONE\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4FreeFrameRxToken (\r
+  IN IP4_LINK_RX_TOKEN      *Token\r
+  )\r
+{\r
+\r
+  NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);\r
+\r
+  gBS->CloseEvent (Token->MnpToken.Event);\r
+  NetFreePool (Token);\r
+}\r
+\r
+\r
+/**\r
+  Remove all the frames on the ARP queue that pass the FrameToCancel,\r
+  that is, either FrameToCancel is NULL or it returns true for the frame.\r
+\r
+  @param  ArpQue                ARP frame to remove the frames from.\r
+  @param  IoStatus              The status returned to the cancelled frames'\r
+                                callback function.\r
+  @param  FrameToCancel         Function to select which frame to cancel.\r
+  @param  Context               Opaque parameter to the FrameToCancel.\r
+\r
+  @return NONE\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4CancelFrameArp (\r
+  IN IP4_ARP_QUE            *ArpQue,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN IP4_FRAME_TO_CANCEL    FrameToCancel, OPTIONAL\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_LINK_TX_TOKEN         *Token;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {\r
+    Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);\r
+\r
+    if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {\r
+      NetListRemoveEntry (Entry);\r
+\r
+      Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context);\r
+      Ip4FreeLinkTxToken (Token);\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Remove all the frames on the interface that pass the FrameToCancel,\r
+  either queued on ARP queues or that have already been delivered to\r
+  MNP and not yet recycled.\r
+\r
+  @param  Interface             Interface to remove the frames from\r
+  @param  IoStatus              The transmit status returned to the frames'\r
+                                callback\r
+  @param  FrameToCancel         Function to select the frame to cancel, NULL to\r
+                                select all\r
+  @param  Context               Opaque parameters passed to FrameToCancel\r
+\r
+  @return NONE\r
+\r
+**/\r
+VOID\r
+Ip4CancelFrames (\r
+  IN IP4_INTERFACE          *Interface,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN IP4_FRAME_TO_CANCEL    FrameToCancel,   OPTIONAL\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_ARP_QUE               *ArpQue;\r
+  IP4_LINK_TX_TOKEN         *Token;\r
+\r
+  //\r
+  // Cancel all the pending frames on ARP requests\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {\r
+    ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);\r
+\r
+    Ip4CancelFrameArp (ArpQue, IoStatus, FrameToCancel, Context);\r
+\r
+    if (NetListIsEmpty (&ArpQue->Frames)) {\r
+      NetListRemoveEntry (Entry);\r
+\r
+      Interface->Arp->Cancel (Interface->Arp, &ArpQue->Ip, ArpQue->OnResolved);\r
+      Ip4FreeArpQue (ArpQue, EFI_ABORTED);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Cancel all the frames that have been delivered to MNP\r
+  // but not yet recycled.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {\r
+    Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);\r
+\r
+    if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {\r
+      NetListRemoveEntry (Entry);\r
+\r
+      Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);\r
+      Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context);\r
+      Ip4FreeLinkTxToken (Token);\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Create an IP4_INTERFACE. Delay the creation of ARP instance until\r
+  the interface is configured.\r
+\r
+  @param  Mnp                   The shared MNP child of this IP4 service binding\r
+                                instance\r
+  @param  Controller            The controller this IP4 service binding instance\r
+                                is installed. Most like the UNDI handle.\r
+  @param  ImageHandle           This driver's image handle\r
+\r
+  @return Point to the created IP4_INTERFACE, otherwise NULL.\r
+\r
+**/\r
+IP4_INTERFACE *\r
+Ip4CreateInterface (\r
+  IN  EFI_MANAGED_NETWORK_PROTOCOL  *Mnp,\r
+  IN  EFI_HANDLE                    Controller,\r
+  IN  EFI_HANDLE                    ImageHandle\r
+  )\r
+{\r
+  IP4_INTERFACE             *Interface;\r
+  EFI_SIMPLE_NETWORK_MODE   SnpMode;\r
+\r
+  Interface = NetAllocatePool (sizeof (IP4_INTERFACE));\r
+\r
+  if ((Interface == NULL) || (Mnp == NULL)) {\r
+    return NULL;\r
+  }\r
+\r
+  Interface->Signature = IP4_INTERFACE_SIGNATURE;\r
+  NetListInit (&Interface->Link);\r
+  Interface->RefCnt     = 1;\r
+\r
+  Interface->Ip         = IP4_ALLZERO_ADDRESS;\r
+  Interface->SubnetMask = IP4_ALLZERO_ADDRESS;\r
+  Interface->Configured = FALSE;\r
+\r
+  Interface->Controller = Controller;\r
+  Interface->Image      = ImageHandle;\r
+  Interface->Mnp        = Mnp;\r
+  Interface->Arp        = NULL;\r
+  Interface->ArpHandle  = NULL;\r
+\r
+  NetListInit (&Interface->ArpQues);\r
+  NetListInit (&Interface->SentFrames);\r
+\r
+  Interface->RecvRequest = NULL;\r
+\r
+  //\r
+  // Get the interface's Mac address and broadcast mac address from SNP\r
+  //\r
+  if (EFI_ERROR (Mnp->GetModeData (Mnp, NULL, &SnpMode))) {\r
+    NetFreePool (Interface);\r
+    return NULL;\r
+  }\r
+\r
+  CopyMem (&Interface->Mac, &SnpMode.CurrentAddress, sizeof (EFI_MAC_ADDRESS));\r
+  CopyMem (&Interface->BroadcastMac, &SnpMode.BroadcastAddress, sizeof (EFI_MAC_ADDRESS));\r
+  Interface->HwaddrLen    = SnpMode.HwAddressSize;\r
+\r
+  NetListInit (&Interface->IpInstances);\r
+  Interface->PromiscRecv = FALSE;\r
+\r
+  return Interface;\r
+}\r
+\r
+\r
+/**\r
+  Set the interface's address, create and configure\r
+  the ARP child if necessary.\r
+\r
+  @param  Interface             The interface to set the address\r
+  @param  IpAddr                The interface's IP address\r
+  @param  SubnetMask            The interface's netmask\r
+\r
+  @retval EFI_SUCCESS           The interface is configured with Ip/netmask pair,\r
+                                and a ARP is created for it.\r
+  @retval Others                Failed to set the interface's address.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4SetAddress (\r
+  IN  IP4_INTERFACE         *Interface,\r
+  IN  IP4_ADDR              IpAddr,\r
+  IN  IP4_ADDR              SubnetMask\r
+  )\r
+{\r
+  EFI_ARP_CONFIG_DATA       ArpConfig;\r
+  EFI_STATUS                Status;\r
+  INTN                      Type;\r
+  INTN                      Len;\r
+  IP4_ADDR                  Netmask;\r
+\r
+  NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);\r
+\r
+  ASSERT (!Interface->Configured);\r
+\r
+  //\r
+  // Set the ip/netmask, then compute the subnet broadcast\r
+  // and network broadcast for easy access. When computing\r
+  // nework broadcast, the subnet mask is most like longer\r
+  // than the default netmask (not subneted) as defined in\r
+  // RFC793. If that isn't the case, we are aggregating the\r
+  // networks, use the subnet's mask instead.\r
+  //\r
+  Interface->Ip             = IpAddr;\r
+  Interface->SubnetMask     = SubnetMask;\r
+  Interface->SubnetBrdcast  = (IpAddr | ~SubnetMask);\r
+\r
+  Type                      = NetGetIpClass (IpAddr);\r
+  Len                       = NetGetMaskLength (SubnetMask);\r
+  Netmask                   = mIp4AllMasks[NET_MIN (Len, Type << 3)];\r
+  Interface->NetBrdcast     = (IpAddr | ~Netmask);\r
+\r
+  //\r
+  // If the address is NOT all zero, create then configure an ARP child.\r
+  // Pay attention: DHCP configures its station address as 0.0.0.0/0\r
+  //\r
+  Interface->Arp            = NULL;\r
+  Interface->ArpHandle      = NULL;\r
+\r
+  if (IpAddr != IP4_ALLZERO_ADDRESS) {\r
+    Status = NetLibCreateServiceChild (\r
+               Interface->Controller,\r
+               Interface->Image,\r
+               &gEfiArpServiceBindingProtocolGuid,\r
+               &Interface->ArpHandle\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;;\r
+    }\r
+\r
+    Status = gBS->OpenProtocol (\r
+                    Interface->ArpHandle,\r
+                    &gEfiArpProtocolGuid,\r
+                    (VOID **) &Interface->Arp,\r
+                    Interface->Image,\r
+                    Interface->Controller,\r
+                    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    IpAddr                    = HTONL (IpAddr);\r
+    ArpConfig.SwAddressType   = IP4_ETHER_PROTO;\r
+    ArpConfig.SwAddressLength = 4;\r
+    ArpConfig.StationAddress  = &IpAddr;\r
+    ArpConfig.EntryTimeOut    = 0;\r
+    ArpConfig.RetryCount      = 0;\r
+    ArpConfig.RetryTimeOut    = 0;\r
+\r
+    Status = Interface->Arp->Configure (Interface->Arp, &ArpConfig);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->CloseProtocol (\r
+            Interface->ArpHandle,\r
+            &gEfiArpProtocolGuid,\r
+            Interface->Image,\r
+            Interface->Controller\r
+            );\r
+\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  Interface->Configured = TRUE;\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  NetLibDestroyServiceChild (\r
+    Interface->Controller,\r
+    Interface->Image,\r
+    &gEfiArpServiceBindingProtocolGuid,\r
+    &Interface->ArpHandle\r
+    );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Fileter function to cancel all the frame related to an IP instance.\r
+\r
+  @param  Frame                 The transmit request to test whether to cancel\r
+  @param  Context               The context which is the Ip instance that issued\r
+                                the transmit.\r
+\r
+  @retval TRUE                  The frame belongs to this instance and is to be\r
+                                removed\r
+  @retval FALSE                 The frame doesn't belong to this instance.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+Ip4CancelInstanceFrame (\r
+  IN IP4_LINK_TX_TOKEN *Frame,\r
+  IN VOID              *Context\r
+  )\r
+{\r
+  if (Frame->IpInstance == (IP4_PROTOCOL *) Context) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+\r
+/**\r
+  If there is a pending receive request, cancel it. Don't call\r
+  the receive request's callback because this function can be only\r
+  called if the instance or driver is tearing itself down. It\r
+  doesn't make sense to call it back. But it is necessary to call\r
+  the transmit token's callback to give it a chance to free the\r
+  packet and update the upper layer's transmit request status, say\r
+  that from the UDP.\r
+\r
+  @param  Interface             The interface used by the IpInstance\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4CancelReceive (\r
+  IN IP4_INTERFACE          *Interface\r
+  )\r
+{\r
+  EFI_TPL                   OldTpl;\r
+  IP4_LINK_RX_TOKEN         *Token;\r
+\r
+  if ((Token = Interface->RecvRequest) != NULL) {\r
+    OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+    Interface->RecvRequest = NULL;\r
+    Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);\r
+    Ip4FreeFrameRxToken (Token);\r
+\r
+    NET_RESTORE_TPL (OldTpl);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Free the interface used by IpInstance. All the IP instance with\r
+  the same Ip/Netmask pair share the same interface. It is reference\r
+  counted. All the frames haven't been sent will be cancelled.\r
+  Because the IpInstance is optional, the caller must remove\r
+  IpInstance from the interface's instance list itself.\r
+\r
+  @param  Interface             The interface used by the IpInstance\r
+  @param  IpInstance            The Ip instance that free the interface. NULL if\r
+                                the  Ip driver is releasing the default interface.\r
+\r
+  @retval EFI_SUCCESS           The interface use IpInstance is freed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4FreeInterface (\r
+  IN  IP4_INTERFACE         *Interface,\r
+  IN  IP4_PROTOCOL          *IpInstance           OPTIONAL\r
+  )\r
+{\r
+  NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);\r
+  ASSERT (Interface->RefCnt > 0);\r
+\r
+  //\r
+  // Remove all the pending transmit token related to this IP instance.\r
+  //\r
+  Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, IpInstance);\r
+\r
+  if (--Interface->RefCnt > 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Destory the interface if this is the last IP instance that\r
+  // has the address. Remove all the system transmitted packets\r
+  // from this interface, cancel the receive request if there is\r
+  // one, and destory the ARP requests.\r
+  //\r
+  Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, NULL);\r
+  Ip4CancelReceive (Interface);\r
+\r
+  ASSERT (NetListIsEmpty (&Interface->IpInstances));\r
+  ASSERT (NetListIsEmpty (&Interface->ArpQues));\r
+  ASSERT (NetListIsEmpty (&Interface->SentFrames));\r
+\r
+  if (Interface->Arp != NULL) {\r
+    gBS->CloseProtocol (\r
+          Interface->ArpHandle,\r
+          &gEfiArpProtocolGuid,\r
+          Interface->Image,\r
+          Interface->Controller\r
+          );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Interface->Controller,\r
+      Interface->Image,\r
+      &gEfiArpServiceBindingProtocolGuid,\r
+      Interface->ArpHandle\r
+      );\r
+  }\r
+\r
+  NetListRemoveEntry (&Interface->Link);\r
+  NetFreePool (Interface);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Callback function when ARP request are finished. It will cancelled\r
+  all the queued frame if the ARP requests failed. Or transmit them\r
+  if the request succeed.\r
+\r
+  @param  Event                 The Arp request event\r
+  @param  Context               The context of the callback, a point to the ARP\r
+                                queue\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Ip4OnArpResolved (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_ARP_QUE               *ArpQue;\r
+  IP4_INTERFACE             *Interface;\r
+  IP4_LINK_TX_TOKEN         *Token;\r
+  EFI_STATUS                Status;\r
+\r
+  ArpQue = (IP4_ARP_QUE *) Context;\r
+  NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);\r
+\r
+  NetListRemoveEntry (&ArpQue->Link);\r
+\r
+  //\r
+  // ARP resolve failed for some reason. Release all the frame\r
+  // and ARP queue itself. Ip4FreeArpQue will call the frame's\r
+  // owner back.\r
+  //\r
+  if (NET_MAC_EQUAL (&ArpQue->Mac, &mZeroMacAddress, ArpQue->Interface->HwaddrLen)) {\r
+    Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);\r
+\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // ARP resolve succeeded, Transmit all the frame. Release the ARP\r
+  // queue. It isn't necessary for us to cache the ARP binding because\r
+  // we always check the ARP cache first before transmit.\r
+  //\r
+  Interface = ArpQue->Interface;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {\r
+    NetListRemoveEntry (Entry);\r
+\r
+    Token         = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);\r
+    CopyMem (&Token->DstMac, &ArpQue->Mac, sizeof (EFI_MAC_ADDRESS));\r
+\r
+    Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context);\r
+\r
+      Ip4FreeLinkTxToken (Token);\r
+      continue;\r
+    }\r
+\r
+    NetListInsertTail (&Interface->SentFrames, &Token->Link);\r
+  }\r
+\r
+  Ip4FreeArpQue (ArpQue, EFI_SUCCESS);\r
+}\r
+\r
+\r
+/**\r
+  Callback funtion when frame transmission is finished. It will\r
+  call the frame owner's callback function to tell it the result.\r
+\r
+  @param  Event                 The transmit token's event\r
+  @param  Context               Context which is point to the token.\r
+\r
+  @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Ip4OnFrameSent (\r
+  IN EFI_EVENT               Event,\r
+  IN VOID                    *Context\r
+  )\r
+{\r
+  IP4_LINK_TX_TOKEN         *Token;\r
+\r
+  Token = (IP4_LINK_TX_TOKEN *) Context;\r
+  NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);\r
+\r
+  NetListRemoveEntry (&Token->Link);\r
+\r
+  Token->CallBack (\r
+          Token->IpInstance,\r
+          Token->Packet,\r
+          Token->MnpToken.Status,\r
+          0,\r
+          Token->Context\r
+          );\r
+\r
+  Ip4FreeLinkTxToken (Token);\r
+}\r
+\r
+\r
+\r
+/**\r
+  Send a frame from the interface. If the next hop is broadcast or\r
+  multicast address, it is transmitted immediately. If the next hop\r
+  is a unicast, it will consult ARP to resolve the NextHop's MAC.\r
+  If some error happened, the CallBack won't be called. So, the caller\r
+  must test the return value, and take action when there is an error.\r
+\r
+  @param  Interface             The interface to send the frame from\r
+  @param  IpInstance            The IP child that request the transmission.  NULL\r
+                                if it is the IP4 driver itself.\r
+  @param  Packet                The packet to transmit.\r
+  @param  NextHop               The immediate destination to transmit the packet\r
+                                to.\r
+  @param  CallBack              Function to call back when transmit finished.\r
+  @param  Context               Opaque parameter to the call back.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to send the frame\r
+  @retval EFI_NO_MAPPING        Can't resolve the MAC for the nexthop\r
+  @retval EFI_SUCCESS           The packet is successfully transmitted.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4SendFrame (\r
+  IN  IP4_INTERFACE         *Interface,\r
+  IN  IP4_PROTOCOL          *IpInstance,      OPTIONAL\r
+  IN  NET_BUF               *Packet,\r
+  IN  IP4_ADDR              NextHop,\r
+  IN  IP4_FRAME_CALLBACK    CallBack,\r
+  IN  VOID                  *Context\r
+  )\r
+{\r
+  IP4_LINK_TX_TOKEN         *Token;\r
+  NET_LIST_ENTRY            *Entry;\r
+  IP4_ARP_QUE               *ArpQue;\r
+  EFI_ARP_PROTOCOL          *Arp;\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT (Interface->Configured);\r
+\r
+  Token = Ip4WrapLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);\r
+\r
+  if (Token == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Get the destination MAC address for multicast and broadcasts.\r
+  // Don't depend on ARP to solve the address since there maybe no\r
+  // ARP at all. Ip4Output has set NextHop to 255.255.255.255 for\r
+  // all the broadcasts.\r
+  //\r
+  if (NextHop == IP4_ALLONE_ADDRESS) {\r
+    CopyMem (&Token->DstMac, &Interface->BroadcastMac, sizeof (EFI_MAC_ADDRESS));\r
+    goto SEND_NOW;\r
+\r
+  } else if (IP4_IS_MULTICAST (NextHop)) {\r
+\r
+    Status = Ip4GetMulticastMac (Interface->Mnp, NextHop, &Token->DstMac);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    goto SEND_NOW;\r
+  }\r
+\r
+  //\r
+  // Can only send out multicast/broadcast if the IP address is zero\r
+  //\r
+  if ((Arp = Interface->Arp) == NULL) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // First check whether this binding is in the ARP cache.\r
+  //\r
+  NextHop = HTONL (NextHop);\r
+  Status  = Arp->Request (Arp, &NextHop, NULL, &Token->DstMac);\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    goto SEND_NOW;\r
+\r
+  } else if (Status != EFI_NOT_READY) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Have to do asynchronous ARP resolution. First check\r
+  // whether there is already a pending request.\r
+  //\r
+  ArpQue = NULL;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {\r
+    ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);\r
+\r
+    if (ArpQue->Ip == NextHop) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Found a pending ARP request, enqueue the frame then return\r
+  //\r
+  if (Entry != &Interface->ArpQues) {\r
+    NetListInsertTail (&ArpQue->Frames, &Token->Link);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // First frame to NextHop, issue an asynchronous ARP requests\r
+  //\r
+  ArpQue = Ip4CreateArpQue (Interface, NextHop);\r
+\r
+  if (ArpQue == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Arp->Request (Arp, &ArpQue->Ip, ArpQue->OnResolved, ArpQue->Mac.Addr);\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {\r
+    Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NetListInsertHead (&ArpQue->Frames, &Token->Link);\r
+  NetListInsertHead (&Interface->ArpQues, &ArpQue->Link);\r
+  return EFI_SUCCESS;\r
+\r
+SEND_NOW:\r
+  Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NetListInsertTail (&Interface->SentFrames, &Token->Link);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  Ip4FreeLinkTxToken (Token);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Call back function when the received packet is freed.\r
+  Check Ip4OnFrameReceived for information.\r
+\r
+  @param  Context               Context, which is the IP4_LINK_RX_TOKEN.\r
+\r
+  @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4RecycleFrame (\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP4_LINK_RX_TOKEN         *Frame;\r
+\r
+  Frame = (IP4_LINK_RX_TOKEN *) Context;\r
+  NET_CHECK_SIGNATURE (Frame, IP4_FRAME_RX_SIGNATURE);\r
+\r
+  gBS->SignalEvent (Frame->MnpToken.Packet.RxData->RecycleEvent);\r
+  Ip4FreeFrameRxToken (Frame);\r
+}\r
+\r
+\r
+/**\r
+  Received a frame from MNP, wrap it in net buffer then deliver\r
+  it to IP's input function. The ownship of the packet also\r
+  transferred to IP. When Ip is finished with this packet, it\r
+  will call NetbufFree to release the packet, NetbufFree will\r
+  again call the Ip4RecycleFrame to signal MNP's event and free\r
+  the token used.\r
+\r
+  @param  Event                 The receive event delivered to MNP for receive.\r
+  @param  Context               Context for the callback.\r
+\r
+  @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Ip4OnFrameReceived (\r
+  IN EFI_EVENT                Event,\r
+  IN VOID                     *Context\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
+  EFI_MANAGED_NETWORK_RECEIVE_DATA      *MnpRxData;\r
+  IP4_LINK_RX_TOKEN                     *Token;\r
+  NET_FRAGMENT                          Netfrag;\r
+  NET_BUF                               *Packet;\r
+  UINT32                                Flag;\r
+\r
+  Token = (IP4_LINK_RX_TOKEN *) Context;\r
+  NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);\r
+\r
+  //\r
+  // First clear the interface's receive request in case the\r
+  // caller wants to call Ip4ReceiveFrame in the callback.\r
+  //\r
+  Token->Interface->RecvRequest = NULL;\r
+\r
+  MnpToken  = &Token->MnpToken;\r
+  MnpRxData = MnpToken->Packet.RxData;\r
+\r
+  if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {\r
+    Token->CallBack (Token->IpInstance, NULL, MnpToken->Status, 0, Token->Context);\r
+    Ip4FreeFrameRxToken (Token);\r
+\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Wrap the frame in a net buffer then deliever it to IP input.\r
+  // IP will reassemble the packet, and deliver it to upper layer\r
+  //\r
+  Netfrag.Len  = MnpRxData->DataLength;\r
+  Netfrag.Bulk = MnpRxData->PacketData;\r
+\r
+  Packet = NetbufFromExt (&Netfrag, 1, 0, IP4_MAX_HEADLEN, Ip4RecycleFrame, Token);\r
+\r
+  if (Packet == NULL) {\r
+    gBS->SignalEvent (MnpRxData->RecycleEvent);\r
+\r
+    Token->CallBack (Token->IpInstance, NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);\r
+    Ip4FreeFrameRxToken (Token);\r
+\r
+    return ;\r
+  }\r
+\r
+  Flag  = (MnpRxData->BroadcastFlag ? IP4_LINK_BROADCAST : 0);\r
+  Flag |= (MnpRxData->MulticastFlag ? IP4_LINK_MULTICAST : 0);\r
+  Flag |= (MnpRxData->PromiscuousFlag ? IP4_LINK_PROMISC : 0);\r
+\r
+  Token->CallBack (Token->IpInstance, Packet, EFI_SUCCESS, Flag, Token->Context);\r
+}\r
+\r
+\r
+/**\r
+  Request to receive the packet from the interface.\r
+\r
+  @param  Interface             The interface to receive the frames from\r
+  @param  IpInstance            The instance that requests the receive. NULL for\r
+                                the driver itself.\r
+  @param  CallBack              Function to call when receive finished.\r
+  @param  Context               Opaque parameter to the callback\r
+\r
+  @retval EFI_ALREADY_STARTED   There is already a pending receive request.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to receive\r
+  @retval EFI_SUCCESS           The recieve request has been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4ReceiveFrame (\r
+  IN  IP4_INTERFACE         *Interface,\r
+  IN  IP4_PROTOCOL          *IpInstance,      OPTIONAL\r
+  IN  IP4_FRAME_CALLBACK    CallBack,\r
+  IN  VOID                  *Context\r
+  )\r
+{\r
+  IP4_LINK_RX_TOKEN *Token;\r
+  EFI_STATUS        Status;\r
+\r
+  NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);\r
+\r
+  if (Interface->RecvRequest != NULL) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  Token = Ip4CreateLinkRxToken (Interface, IpInstance, CallBack, Context);\r
+\r
+  if (Token == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = Interface->Mnp->Receive (Interface->Mnp, &Token->MnpToken);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Ip4FreeFrameRxToken (Token);\r
+    return Status;\r
+  }\r
+\r
+  Interface->RecvRequest = Token;\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h
new file mode 100644 (file)
index 0000000..a324c82
--- /dev/null
@@ -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 (file)
index 0000000..135efa6
--- /dev/null
@@ -0,0 +1,629 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+Module Name:\r
+\r
+  Ip4Igmp.c\r
+\r
+Abstract:\r
+\r
+  This file implements the RFC2236: IGMP v2\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+//\r
+// Route Alert option in IGMP report to direct routers to\r
+// examine the packet more closely.\r
+//\r
+UINT32  mRouteAlertOption = 0x00000494;\r
+\r
+\r
+/**\r
+  Init the IGMP control data of the IP4 service instance, configure\r
+  MNP to receive ALL SYSTEM multicast.\r
+\r
+  @param  IpSb                   The IP4 service whose IGMP is to be initialized.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource to initialize IGMP.\r
+  @retval Others                 Failed to initialize the IGMP of IpSb.\r
+  @retval EFI_SUCCESS            IGMP of the IpSb is successfully initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4InitIgmp (\r
+  IN IP4_SERVICE            *IpSb\r
+  )\r
+{\r
+  IGMP_SERVICE_DATA             *IgmpCtrl;\r
+  EFI_MANAGED_NETWORK_PROTOCOL  *Mnp;\r
+  IGMP_GROUP                    *Group;\r
+  EFI_STATUS                    Status;\r
+\r
+  IgmpCtrl = &IpSb->IgmpCtrl;\r
+\r
+  //\r
+  // Configure MNP to receive ALL_SYSTEM multicast\r
+  //\r
+  Group    = NetAllocatePool (sizeof (IGMP_GROUP));\r
+\r
+  if (Group == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Mnp               = IpSb->Mnp;\r
+\r
+  Group->Address    = IP4_ALLSYSTEM_ADDRESS;\r
+  Group->RefCnt     = 1;\r
+  Group->DelayTime  = 0;\r
+  Group->ReportByUs = FALSE;\r
+\r
+  Status = Ip4GetMulticastMac (Mnp, IP4_ALLSYSTEM_ADDRESS, &Group->Mac);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NetListInsertHead (&IgmpCtrl->Groups, &Group->Link);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  NetFreePool (Group);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Find the IGMP_GROUP structure which contains the status of multicast\r
+  group Address in this IGMP control block\r
+\r
+  @param  IgmpCtrl               The IGMP control block to search from\r
+  @param  Address                The multicast address to search\r
+\r
+  @return NULL if the multicast address isn't in the IGMP control block. Otherwise\r
+  @return the point to the IGMP_GROUP which contains the status of multicast group\r
+  @return for Address.\r
+\r
+**/\r
+IGMP_GROUP *\r
+Ip4FindGroup (\r
+  IN IGMP_SERVICE_DATA      *IgmpCtrl,\r
+  IN IP4_ADDR               Address\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  IGMP_GROUP                *Group;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
+    Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
+\r
+    if (Group->Address == Address) {\r
+      return Group;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Count the number of IP4 multicast groups that are mapped to the\r
+  same MAC address. Several IP4 multicast address may be mapped to\r
+  the same MAC address.\r
+\r
+  @param  IgmpCtrl               The IGMP control block to search in\r
+  @param  Mac                    The MAC address to search\r
+\r
+  @return The number of the IP4 multicast group that mapped to the same\r
+  @return multicast group Mac.\r
+\r
+**/\r
+INTN\r
+Ip4FindMac (\r
+  IN IGMP_SERVICE_DATA      *IgmpCtrl,\r
+  IN EFI_MAC_ADDRESS        *Mac\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  IGMP_GROUP                *Group;\r
+  INTN                      Count;\r
+\r
+  Count = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
+    Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
+\r
+    if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {\r
+      Count++;\r
+    }\r
+  }\r
+\r
+  return Count;\r
+}\r
+\r
+\r
+/**\r
+  Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.\r
+\r
+  @param  IpSb                   The IP4 service instance that requests the\r
+                                 transmission\r
+  @param  Dst                    The destinaton to send to\r
+  @param  Type                   The IGMP message type, such as IGMP v2 membership\r
+                                 report\r
+  @param  Group                  The group address in the IGMP message head.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to build the message\r
+  @retval EFI_SUCCESS            The IGMP message is successfully send\r
+  @retval Others                 Failed to send the IGMP message.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4SendIgmpMessage (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_ADDR               Dst,\r
+  IN UINT8                  Type,\r
+  IN IP4_ADDR               Group\r
+  )\r
+{\r
+  IP4_HEAD                  Head;\r
+  NET_BUF                   *Packet;\r
+  IGMP_HEAD                 *Igmp;\r
+\r
+  //\r
+  // Allocate a net buffer to hold the message\r
+  //\r
+  Packet = NetbufAlloc (IP4_MAX_HEADLEN + sizeof (IGMP_HEAD));\r
+\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Fill in the IGMP and IP header, then transmit the message\r
+  //\r
+  NetbufReserve (Packet, IP4_MAX_HEADLEN);\r
+\r
+  Igmp = (IGMP_HEAD *) NetbufAllocSpace (Packet, sizeof (IGMP_HEAD), FALSE);\r
+\r
+  Igmp->Type        = Type;\r
+  Igmp->MaxRespTime = 0;\r
+  Igmp->Checksum    = 0;\r
+  Igmp->Group       = HTONL (Group);\r
+  Igmp->Checksum    = ~NetblockChecksum ((UINT8 *) Igmp, sizeof (IGMP_HEAD));\r
+\r
+  Head.Tos          = 0;\r
+  Head.Protocol     = IP4_PROTO_IGMP;\r
+  Head.Ttl          = 1;\r
+  Head.Fragment     = 0;\r
+  Head.Dst          = Dst;\r
+  Head.Src          = IP4_ALLZERO_ADDRESS;\r
+\r
+  return Ip4Output (\r
+           IpSb,\r
+           NULL,\r
+           Packet,\r
+           &Head,\r
+           (UINT8 *) &mRouteAlertOption,\r
+           sizeof (UINT32),\r
+           IP4_ALLZERO_ADDRESS,\r
+           Ip4SysPacketSent,\r
+           NULL\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Send an IGMP membership report. Depends on whether the server is\r
+  v1 or v2, it will send either a V1 or V2 membership report.\r
+\r
+  @param  IpSb                   The IP4 service instance that requests the\r
+                                 transmission.\r
+  @param  Group                  The group address to report\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to build the message\r
+  @retval EFI_SUCCESS            The IGMP report message is successfully send\r
+  @retval Others                 Failed to send the report.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4SendIgmpReport (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_ADDR               Group\r
+  )\r
+{\r
+  if (IpSb->IgmpCtrl.Igmpv1QuerySeen != 0) {\r
+    return Ip4SendIgmpMessage (IpSb, Group, IGMP_V1_MEMBERSHIP_REPORT, Group);\r
+  } else {\r
+    return Ip4SendIgmpMessage (IpSb, Group, IGMP_V2_MEMBERSHIP_REPORT, Group);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Join the multicast group on behavior of this IP4 child\r
+\r
+  @param  IpInstance             The IP4 child that wants to join the group\r
+  @param  Address                The group to join\r
+\r
+  @retval EFI_SUCCESS            Successfully join the multicast group\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources\r
+  @retval Others                 Failed to join the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4JoinGroup (\r
+  IN IP4_PROTOCOL           *IpInstance,\r
+  IN IP4_ADDR               Address\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_PROTOCOL  *Mnp;\r
+  IP4_SERVICE                   *IpSb;\r
+  IGMP_SERVICE_DATA             *IgmpCtrl;\r
+  IGMP_GROUP                    *Group;\r
+  EFI_STATUS                    Status;\r
+\r
+  IpSb      = IpInstance->Service;\r
+  IgmpCtrl  = &IpSb->IgmpCtrl;\r
+  Mnp       = IpSb->Mnp;\r
+\r
+  //\r
+  // If the IP service already is a member in the group, just\r
+  // increase the refernce count and return.\r
+  //\r
+  Group     = Ip4FindGroup (IgmpCtrl, Address);\r
+\r
+  if (Group != NULL) {\r
+    Group->RefCnt++;\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Otherwise, create a new IGMP_GROUP,  Get the multicast's MAC address,\r
+  // send a report, then direct MNP to receive the multicast.\r
+  //\r
+  Group = NetAllocatePool (sizeof (IGMP_GROUP));\r
+\r
+  if (Group == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Group->Address    = Address;\r
+  Group->RefCnt     = 1;\r
+  Group->DelayTime  = IGMP_UNSOLICIATED_REPORT;\r
+  Group->ReportByUs = TRUE;\r
+\r
+  Status = Ip4GetMulticastMac (Mnp, Address, &Group->Mac);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip4SendIgmpReport (IpSb, Address);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NetListInsertHead (&IgmpCtrl->Groups, &Group->Link);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  NetFreePool (Group);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Leave the IP4 multicast group on behavior of IpInstance.\r
+\r
+  @param  IpInstance             The IP4 child that wants to leave the group\r
+                                 address\r
+  @param  Address                The group address to leave\r
+\r
+  @retval EFI_NOT_FOUND          The IP4 service instance isn't in the group\r
+  @retval EFI_SUCCESS            Successfully leave the multicast group.\r
+  @retval Others                 Failed to leave the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4LeaveGroup (\r
+  IN IP4_PROTOCOL           *IpInstance,\r
+  IN IP4_ADDR               Address\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_PROTOCOL  *Mnp;\r
+  IP4_SERVICE                   *IpSb;\r
+  IGMP_SERVICE_DATA             *IgmpCtrl;\r
+  IGMP_GROUP                    *Group;\r
+  EFI_STATUS                    Status;\r
+\r
+  IpSb      = IpInstance->Service;\r
+  IgmpCtrl  = &IpSb->IgmpCtrl;\r
+  Mnp       = IpSb->Mnp;\r
+\r
+  Group     = Ip4FindGroup (IgmpCtrl, Address);\r
+\r
+  if (Group == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // If more than one instance is in the group, decrease\r
+  // the RefCnt then return.\r
+  //\r
+  if (--Group->RefCnt > 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // If multiple IP4 group addresses are mapped to the same\r
+  // multicast MAC address, don't configure the MNP to leave\r
+  // the MAC.\r
+  //\r
+  if (Ip4FindMac (IgmpCtrl, &Group->Mac) == 1) {\r
+    Status = Mnp->Groups (Mnp, FALSE, &Group->Mac);\r
+\r
+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Send a leave report if the membership is reported by us\r
+  // and we are talking IGMPv2.\r
+  //\r
+  if (Group->ReportByUs && !IgmpCtrl->Igmpv1QuerySeen) {\r
+    Ip4SendIgmpMessage (IpSb, IP4_ALLROUTER_ADDRESS, IGMP_LEAVE_GROUP, Group->Address);\r
+  }\r
+\r
+  NetListRemoveEntry (&Group->Link);\r
+  NetFreePool (Group);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Handle the received IGMP message for the IP4 service instance.\r
+\r
+  @param  IpSb                   The IP4 service instance that received the message\r
+  @param  Head                   The IP4 header of the received message\r
+  @param  Packet                 The IGMP message, without IP4 header\r
+\r
+  @retval EFI_INVALID_PARAMETER  The IGMP message is malformated.\r
+  @retval EFI_SUCCESS            The IGMP message is successfully processed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4IgmpHandle (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IGMP_SERVICE_DATA         *IgmpCtrl;\r
+  IGMP_HEAD                 Igmp;\r
+  IGMP_GROUP                *Group;\r
+  IP4_ADDR                  Address;\r
+  NET_LIST_ENTRY            *Entry;\r
+\r
+  IgmpCtrl = &IpSb->IgmpCtrl;\r
+\r
+  //\r
+  // Must checksum over the whole packet, later IGMP version\r
+  // may employ message longer than 8 bytes. IP's header has\r
+  // already been trimmed off.\r
+  //\r
+  if ((Packet->TotalSize < sizeof (Igmp)) || (NetbufChecksum (Packet) != 0)) {\r
+    NetbufFree (Packet);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Copy the packet in case it is fragmented\r
+  //\r
+  NetbufCopy (Packet, 0, sizeof (IGMP_HEAD), (UINT8 *)&Igmp);\r
+\r
+  switch (Igmp.Type) {\r
+  case IGMP_MEMBERSHIP_QUERY:\r
+    //\r
+    // If MaxRespTIme is zero, it is most likely that we are\r
+    // talking to a V1 router\r
+    //\r
+    if (Igmp.MaxRespTime == 0) {\r
+      IgmpCtrl->Igmpv1QuerySeen = IGMP_V1ROUTER_PRESENT;\r
+      Igmp.MaxRespTime          = 100;\r
+    }\r
+\r
+    //\r
+    // Igmp is ticking once per second but MaxRespTime is in\r
+    // the unit of 100ms.\r
+    //\r
+    Igmp.MaxRespTime /= 10;\r
+    Address = NTOHL (Igmp.Group);\r
+\r
+    if (Address == IP4_ALLSYSTEM_ADDRESS) {\r
+      break;\r
+    }\r
+\r
+    NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
+      Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
+\r
+      //\r
+      // If address is all zero, all the memberships will be reported.\r
+      // otherwise only one is reported.\r
+      //\r
+      if ((Address == IP4_ALLZERO_ADDRESS) || (Address == Group->Address)) {\r
+        //\r
+        // If the timer is pending, only update it if the time left\r
+        // is longer than the MaxRespTime. TODO: randomize the DelayTime.\r
+        //\r
+        if ((Group->DelayTime == 0) || (Group->DelayTime > Igmp.MaxRespTime)) {\r
+          Group->DelayTime = NET_MAX (1, Igmp.MaxRespTime);\r
+        }\r
+      }\r
+    }\r
+\r
+    break;\r
+\r
+  case IGMP_V1_MEMBERSHIP_REPORT:\r
+  case IGMP_V2_MEMBERSHIP_REPORT:\r
+    Address = NTOHL (Igmp.Group);\r
+    Group   = Ip4FindGroup (IgmpCtrl, Address);\r
+\r
+    if ((Group != NULL) && (Group->DelayTime > 0)) {\r
+      Group->DelayTime  = 0;\r
+      Group->ReportByUs = FALSE;\r
+    }\r
+\r
+    break;\r
+  }\r
+\r
+  NetbufFree (Packet);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  The periodical timer function for IGMP. It does the following\r
+  things:\r
+  1. Decrease the Igmpv1QuerySeen to make it possible to refresh\r
+  the IGMP server type.\r
+  2. Decrease the report timer for each IGMP group in "delaying\r
+  member" state.\r
+\r
+  @param  IpSb                   The IP4 service instance that is ticking\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4IgmpTicking (\r
+  IN IP4_SERVICE            *IpSb\r
+  )\r
+{\r
+  IGMP_SERVICE_DATA         *IgmpCtrl;\r
+  NET_LIST_ENTRY            *Entry;\r
+  IGMP_GROUP                *Group;\r
+\r
+  IgmpCtrl = &IpSb->IgmpCtrl;\r
+\r
+  if (IgmpCtrl->Igmpv1QuerySeen > 0) {\r
+    IgmpCtrl->Igmpv1QuerySeen--;\r
+  }\r
+\r
+  //\r
+  // Decrease the report timer for each IGMP group in "delaying member"\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
+    Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
+    ASSERT (Group->DelayTime >= 0);\r
+\r
+    if (Group->DelayTime > 0) {\r
+      Group->DelayTime--;\r
+\r
+      if (Group->DelayTime == 0) {\r
+        Ip4SendIgmpReport (IpSb, Group->Address);\r
+        Group->ReportByUs = TRUE;\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Add a group address to the array of group addresses.\r
+  The caller should make sure that no duplicated address\r
+  existed in the array. Although the function doesn't\r
+  assume the byte order of the both Source and Addr, the\r
+  network byte order is used by the caller.\r
+\r
+  @param  Source                 The array of group addresses to add to\r
+  @param  Count                  The number of group addresses in the Source\r
+  @param  Addr                   The IP4 multicast address to add\r
+\r
+  @return NULL if failed to allocate memory for the new groups,\r
+  @return otherwise the new combined group addresses.\r
+\r
+**/\r
+IP4_ADDR *\r
+Ip4CombineGroups (\r
+  IN  IP4_ADDR              *Source,\r
+  IN  UINT32                Count,\r
+  IN  IP4_ADDR              Addr\r
+  )\r
+{\r
+  IP4_ADDR                  *Groups;\r
+\r
+  Groups = NetAllocatePool (sizeof (IP4_ADDR) * (Count + 1));\r
+\r
+  if (Groups == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NetCopyMem (Groups, Source, Count * sizeof (IP4_ADDR));\r
+  Groups[Count] = Addr;\r
+\r
+  return Groups;\r
+}\r
+\r
+\r
+/**\r
+  Remove a group address frome the array of group addresses.\r
+  Although the function doesn't assume the byte order of the\r
+  both Groups and Addr, the network byte order is used by\r
+  the caller.\r
+\r
+  @param  Groups                 The array of group addresses to remove from\r
+  @param  Count                  The number of group addresses in the Groups\r
+  @param  Addr                   The IP4 multicast address to remove\r
+\r
+  @return The nubmer of group addresses in the Groups after remove.\r
+  @return It is Count if the Addr isn't in the Groups.\r
+\r
+**/\r
+INTN\r
+Ip4RemoveGroupAddr (\r
+  IN IP4_ADDR               *Groups,\r
+  IN UINT32                 Count,\r
+  IN IP4_ADDR               Addr\r
+  )\r
+{\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < Count; Index++) {\r
+    if (Groups[Index] == Addr) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  while (Index < Count - 1) {\r
+    Groups[Index] = Groups[Index + 1];\r
+    Index++;\r
+  }\r
+\r
+  return Index;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h
new file mode 100644 (file)
index 0000000..f7cb92c
--- /dev/null
@@ -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 (file)
index 0000000..824524d
--- /dev/null
@@ -0,0 +1,2022 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+Module Name:\r
+\r
+  Ip4Impl.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+\r
+/**\r
+  Get the IP child's current operational data. This can\r
+  all be used to get the underlying MNP and SNP data.\r
+\r
+  @param  This                   The IP4 protocol instance\r
+  @param  Ip4ModeData            The IP4 operation data\r
+  @param  MnpConfigData          The MNP configure data\r
+  @param  SnpModeData            The SNP operation data\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid because This == NULL\r
+  @retval EFI_SUCCESS            The operational parameter is returned.\r
+  @retval Others                 Failed to retrieve the IP4 route table.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp4GetModeData (\r
+  IN  CONST EFI_IP4_PROTOCOL                *This,\r
+  OUT       EFI_IP4_MODE_DATA               *Ip4ModeData,    OPTIONAL\r
+  OUT       EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData,  OPTIONAL\r
+  OUT       EFI_SIMPLE_NETWORK_MODE         *SnpModeData     OPTIONAL\r
+  )\r
+{\r
+  IP4_PROTOCOL              *IpInstance;\r
+  IP4_SERVICE               *IpSb;\r
+  EFI_IP4_CONFIG_DATA       *Config;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  IP4_ADDR                  Ip;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl     = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (Ip4ModeData != NULL) {\r
+    //\r
+    // IsStarted is "whether the EfiIp4Configure has been called".\r
+    // IsConfigured is "whether the station address has been configured"\r
+    //\r
+    Ip4ModeData->IsStarted     = (BOOLEAN)(IpInstance->State == IP4_STATE_CONFIGED);\r
+    CopyMem (&Ip4ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP4_CONFIG_DATA));\r
+    Ip4ModeData->IsConfigured  = FALSE;\r
+\r
+    Ip4ModeData->GroupCount    = IpInstance->GroupCount;\r
+    Ip4ModeData->GroupTable    = (EFI_IPv4_ADDRESS *) IpInstance->Groups;\r
+\r
+    Ip4ModeData->IcmpTypeCount = 23;\r
+    Ip4ModeData->IcmpTypeList  = mIp4SupportedIcmp;\r
+\r
+    Ip4ModeData->RouteTable    = NULL;\r
+    Ip4ModeData->RouteCount    = 0;\r
+\r
+    //\r
+    // return the current station address for this IP child. So,\r
+    // the user can get the default address through this. Some\r
+    // application wants to know it station address even it is\r
+    // using the default one, such as a ftp server.\r
+    //\r
+    if (Ip4ModeData->IsStarted) {\r
+      Config  = &Ip4ModeData->ConfigData;\r
+\r
+      Ip = HTONL (IpInstance->Interface->Ip);\r
+      NetCopyMem (&Config->StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+      Ip = HTONL (IpInstance->Interface->SubnetMask);\r
+      NetCopyMem (&Config->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+      Ip4ModeData->IsConfigured = IpInstance->Interface->Configured;\r
+\r
+      //\r
+      // Build a EFI route table for user from the internal route table.\r
+      //\r
+      Status = Ip4BuildEfiRouteTable (IpInstance);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        NET_RESTORE_TPL (OldTpl);\r
+        return Status;\r
+      }\r
+\r
+      Ip4ModeData->RouteTable = IpInstance->EfiRouteTable;\r
+      Ip4ModeData->RouteCount = IpInstance->EfiRouteCount;\r
+    }\r
+  }\r
+\r
+  if (MnpConfigData != NULL) {\r
+    CopyMem (MnpConfigData, &IpSb->MnpConfigData, sizeof (EFI_MANAGED_NETWORK_CONFIG_DATA));\r
+  }\r
+\r
+  if (SnpModeData != NULL) {\r
+    CopyMem (SnpModeData, &IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));\r
+  }\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Config the MNP parameter used by IP. The IP driver use one MNP\r
+  child to transmit/receive frames. By default, it configures MNP\r
+  to receive unicast/multicast/broadcast. And it will enable/disable\r
+  the promiscous receive according to whether there is IP child\r
+  enable that or not. If Force isn't false, it will iterate through\r
+  all the IP children to check whether the promiscuous receive\r
+  setting has been changed. If it hasn't been changed, it won't\r
+  reconfigure the MNP. If Force is true, the MNP is configured no\r
+  matter whether that is changed or not.\r
+\r
+  @param  IpSb                   The IP4 service instance that is to be changed.\r
+  @param  Force                  Force the configuration or not.\r
+\r
+  @retval EFI_SUCCESS            The MNP is successfully configured/reconfigured.\r
+  @retval Others                 Configuration failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4ServiceConfigMnp (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN BOOLEAN                Force\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *ProtoEntry;\r
+  IP4_INTERFACE             *IpIf;\r
+  IP4_PROTOCOL              *IpInstance;\r
+  BOOLEAN                   Reconfig;\r
+  BOOLEAN                   PromiscReceive;\r
+  EFI_STATUS                Status;\r
+\r
+  Reconfig       = FALSE;\r
+  PromiscReceive = FALSE;\r
+\r
+  if (!Force) {\r
+    //\r
+    // Iterate through the IP children to check whether promiscuous\r
+    // receive setting has been changed. Update the interface's receive\r
+    // filter also.\r
+    //\r
+    NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+\r
+      IpIf              = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
+      IpIf->PromiscRecv = FALSE;\r
+\r
+      NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) {\r
+        IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP4_PROTOCOL, AddrLink);\r
+\r
+        if (IpInstance->ConfigData.AcceptPromiscuous) {\r
+          IpIf->PromiscRecv = TRUE;\r
+          PromiscReceive    = TRUE;\r
+        }\r
+      }\r
+    }\r
+\r
+    //\r
+    // If promiscuous receive isn't changed, it isn't necessary to reconfigure.\r
+    //\r
+    if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    Reconfig  = TRUE;\r
+    IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive;\r
+  }\r
+\r
+  Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData);\r
+\r
+  //\r
+  // recover the original configuration if failed to set the configure.\r
+  //\r
+  if (EFI_ERROR (Status) && Reconfig) {\r
+    IpSb->MnpConfigData.EnablePromiscuousReceive = !PromiscReceive;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The event handle for IP4 auto configuration. If IP is asked\r
+  to reconfigure the default address. The original default\r
+  interface and route table are removed as the default. If there\r
+  is active IP children using the default address, the interface\r
+  will remain valid until all the children have freed their\r
+  references. If IP is signalled when auto configuration is done,\r
+  it will configure the default interface and default route table\r
+  with the configuration information retrieved by IP4_CONFIGURE.\r
+\r
+  @param  Event                  The event that is signalled.\r
+  @param  Context                The IP4 service binding instance.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip4AutoConfigCallBack (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_IP4_CONFIG_PROTOCOL   *Ip4Config;\r
+  EFI_IP4_IPCONFIG_DATA     *Data;\r
+  EFI_IP4_ROUTE_TABLE       *RouteEntry;\r
+  IP4_SERVICE               *IpSb;\r
+  IP4_ROUTE_TABLE           *RouteTable;\r
+  IP4_INTERFACE             *IpIf;\r
+  EFI_STATUS                Status;\r
+  UINTN                     Len;\r
+  UINT32                    Index;\r
+\r
+  IpSb      = (IP4_SERVICE *) Context;\r
+  NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE);\r
+\r
+  Ip4Config = IpSb->Ip4Config;\r
+\r
+  //\r
+  // IP is asked to do the reconfiguration. If the default interface\r
+  // has been configured, release the default interface and route\r
+  // table, then create a new one. If there are some IP children\r
+  // using it, the interface won't be physically freed until all the\r
+  // children have released their reference to it. Also remember to\r
+  // restart the receive on the default address. IP4 driver only receive\r
+  // frames on the default address, and when the default interface is\r
+  // freed, Ip4AcceptFrame won't be informed.\r
+  //\r
+  if (Event == IpSb->ReconfigEvent) {\r
+\r
+    if (IpSb->DefaultInterface->Configured) {\r
+      IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image);\r
+\r
+      if (IpIf == NULL) {\r
+        return;\r
+      }\r
+\r
+      RouteTable = Ip4CreateRouteTable ();\r
+\r
+      if (RouteTable == NULL) {\r
+        Ip4FreeInterface (IpIf, NULL);\r
+        return;\r
+      }\r
+\r
+      Ip4CancelReceive (IpSb->DefaultInterface);\r
+      Ip4FreeInterface (IpSb->DefaultInterface, NULL);\r
+      Ip4FreeRouteTable (IpSb->DefaultRouteTable);\r
+\r
+      IpSb->DefaultInterface  = IpIf;\r
+      NetListInsertHead (&IpSb->Interfaces, &IpIf->Link);\r
+\r
+      IpSb->DefaultRouteTable = RouteTable;\r
+      Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb);\r
+    }\r
+\r
+    Ip4Config->Stop (Ip4Config);\r
+    Ip4Config->Start (Ip4Config, IpSb->DoneEvent, IpSb->ReconfigEvent);\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Get the configure data in two steps: get the length then the data.\r
+  //\r
+  Len = 0;\r
+\r
+  if (Ip4Config->GetData (Ip4Config, &Len, NULL) != EFI_BUFFER_TOO_SMALL) {\r
+    return ;\r
+  }\r
+\r
+  Data = NetAllocatePool (Len);\r
+\r
+  if (Data == NULL) {\r
+    return ;\r
+  }\r
+\r
+  Status = Ip4Config->GetData (Ip4Config, &Len, Data);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  IpIf = IpSb->DefaultInterface;\r
+\r
+  //\r
+  // If the default address has been configured don't change it.\r
+  // This is unlikely to happen if EFI_IP4_CONFIG protocol has\r
+  // informed us to reconfigure each time it wants to change the\r
+  // configuration parameters.\r
+  //\r
+  if (IpIf->Configured) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Set the default interface's address, then add a directed\r
+  // route for it, that is, the route whose nexthop is zero.\r
+  //\r
+  Status = Ip4SetAddress (\r
+             IpIf,\r
+             EFI_NTOHL (Data->StationAddress),\r
+             EFI_NTOHL (Data->SubnetMask)\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Ip4AddRoute (\r
+    IpSb->DefaultRouteTable,\r
+    EFI_NTOHL (Data->StationAddress),\r
+    EFI_NTOHL (Data->SubnetMask),\r
+    IP4_ALLZERO_ADDRESS\r
+    );\r
+\r
+  //\r
+  // Add routes returned by EFI_IP4_CONFIG protocol.\r
+  //\r
+  for (Index = 0; Index < Data->RouteTableSize; Index++) {\r
+    RouteEntry = &Data->RouteTable[Index];\r
+\r
+    Ip4AddRoute (\r
+      IpSb->DefaultRouteTable,\r
+      EFI_NTOHL (RouteEntry->SubnetAddress),\r
+      EFI_NTOHL (RouteEntry->SubnetMask),\r
+      EFI_NTOHL (RouteEntry->GatewayAddress)\r
+      );\r
+  }\r
+\r
+  IpSb->State = IP4_SERVICE_CONFIGED;\r
+\r
+  Ip4SetVariableData (IpSb);\r
+\r
+ON_EXIT:\r
+  NetFreePool (Data);\r
+}\r
+\r
+\r
+/**\r
+  Start the auto configuration for this IP service instance.\r
+  It will locates the EFI_IP4_CONFIG_PROTOCOL, then start the\r
+  auto configuration.\r
+\r
+  @param  IpSb                   The IP4 service instance to configure\r
+\r
+  @retval EFI_SUCCESS            The auto configuration is successfull started\r
+  @retval Others                 Failed to start auto configuration.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4StartAutoConfig (\r
+  IN IP4_SERVICE            *IpSb\r
+  )\r
+{\r
+  EFI_IP4_CONFIG_PROTOCOL   *Ip4Config;\r
+  EFI_STATUS                Status;\r
+\r
+  if (IpSb->State > IP4_SERVICE_UNSTARTED) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Create the DoneEvent and ReconfigEvent to call EFI_IP4_CONFIG\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  NET_TPL_LOCK,\r
+                  Ip4AutoConfigCallBack,\r
+                  IpSb,\r
+                  &IpSb->DoneEvent\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ip4AutoConfigCallBack,\r
+                  IpSb,\r
+                  &IpSb->ReconfigEvent\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto CLOSE_DONE_EVENT;\r
+  }\r
+\r
+  //\r
+  // Open the EFI_IP4_CONFIG protocol then start auto configure\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  IpSb->Controller,\r
+                  &gEfiIp4ConfigProtocolGuid,\r
+                  (VOID **) &Ip4Config,\r
+                  IpSb->Image,\r
+                  IpSb->Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_UNSUPPORTED;\r
+    goto CLOSE_RECONFIG_EVENT;\r
+  }\r
+\r
+  Status = Ip4Config->Start (Ip4Config, IpSb->DoneEvent, IpSb->ReconfigEvent);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseProtocol (\r
+           IpSb->Controller,\r
+           &gEfiIp4ConfigProtocolGuid,\r
+           IpSb->Image,\r
+           IpSb->Controller\r
+           );\r
+\r
+    goto CLOSE_RECONFIG_EVENT;\r
+  }\r
+\r
+  IpSb->Ip4Config = Ip4Config;\r
+  IpSb->State     = IP4_SERVICE_STARTED;\r
+  return Status;\r
+\r
+CLOSE_RECONFIG_EVENT:\r
+  gBS->CloseEvent (IpSb->ReconfigEvent);\r
+  IpSb->ReconfigEvent = NULL;\r
+\r
+CLOSE_DONE_EVENT:\r
+  gBS->CloseEvent (IpSb->DoneEvent);\r
+  IpSb->DoneEvent = NULL;\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Intiialize the IP4_PROTOCOL structure to the unconfigured states.\r
+\r
+  @param  IpSb                   The IP4 service instance.\r
+  @param  IpInstance             The IP4 child instance.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4InitProtocol (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_PROTOCOL           *IpInstance\r
+  )\r
+{\r
+  ASSERT ((IpSb != NULL) && (IpInstance != NULL));\r
+\r
+  NetZeroMem (IpInstance, sizeof (IP4_PROTOCOL));\r
+\r
+  IpInstance->Signature = IP4_PROTOCOL_SIGNATURE;\r
+  CopyMem (&IpInstance->Ip4Proto, &mEfiIp4ProtocolTemplete, sizeof (EFI_IP4_PROTOCOL));\r
+  IpInstance->State     = IP4_STATE_UNCONFIGED;\r
+  IpInstance->Service   = IpSb;\r
+\r
+  NetListInit (&IpInstance->Link);\r
+  NetMapInit  (&IpInstance->RxTokens);\r
+  NetMapInit  (&IpInstance->TxTokens);\r
+  NetListInit (&IpInstance->Received);\r
+  NetListInit (&IpInstance->Delivered);\r
+  NetListInit (&IpInstance->AddrLink);\r
+\r
+  NET_RECYCLE_LOCK_INIT (&IpInstance->RecycleLock);\r
+}\r
+\r
+\r
+/**\r
+  Configure the IP4 child. If the child is already configured,\r
+  change the configuration parameter. Otherwise configure it\r
+  for the first time. The caller should validate the configuration\r
+  before deliver them to it. It also don't do configure NULL.\r
+\r
+  @param  IpInstance             The IP4 child to configure.\r
+  @param  Config                 The configure data.\r
+\r
+  @retval EFI_SUCCESS            The IP4 child is successfully configured.\r
+  @retval EFI_DEVICE_ERROR       Failed to free the pending transive or to\r
+                                 configure  underlying MNP or other errors.\r
+  @retval EFI_NO_MAPPING         The IP4 child is configured to use default\r
+                                 address, but the default address hasn't been\r
+                                 configured. The IP4 child doesn't need to be\r
+                                 reconfigured when default address is configured.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4ConfigProtocol (\r
+  IN  IP4_PROTOCOL        *IpInstance,\r
+  IN  EFI_IP4_CONFIG_DATA *Config\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+  IP4_INTERFACE             *IpIf;\r
+  EFI_STATUS                Status;\r
+  IP4_ADDR                  Ip;\r
+  IP4_ADDR                  Netmask;\r
+\r
+  IpSb = IpInstance->Service;\r
+\r
+  //\r
+  // User is changing packet filters. It must be stopped\r
+  // before the station address can be changed.\r
+  //\r
+  if (IpInstance->State == IP4_STATE_CONFIGED) {\r
+    //\r
+    // Cancel all the pending transmit/receive from upper layer\r
+    //\r
+    Status = Ip4Cancel (IpInstance, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    CopyMem (&IpInstance->ConfigData, Config, sizeof (EFI_IP4_CONFIG_DATA));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Configure a fresh IP4 protocol instance. Create a route table.\r
+  // Each IP child has its own route table, which may point to the\r
+  // default table if it is using default address.\r
+  //\r
+  Status                 = EFI_OUT_OF_RESOURCES;\r
+  IpInstance->RouteTable = Ip4CreateRouteTable ();\r
+\r
+  if (IpInstance->RouteTable == NULL) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Set up the interface.\r
+  //\r
+  NetCopyMem (&Ip, &Config->StationAddress, sizeof (IP4_ADDR));\r
+  NetCopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR));\r
+\r
+  Ip      = NTOHL (Ip);\r
+  Netmask = NTOHL (Netmask);\r
+\r
+  if (!Config->UseDefaultAddress) {\r
+    //\r
+    // Find whether there is already an interface with the same\r
+    // station address. All the instances with the same station\r
+    // address shares one interface.\r
+    //\r
+    IpIf = Ip4FindStationAddress (IpSb, Ip, Netmask);\r
+\r
+    if (IpIf != NULL) {\r
+      NET_GET_REF (IpIf);\r
+\r
+    } else {\r
+      IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image);\r
+\r
+      if (IpIf == NULL) {\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      Status = Ip4SetAddress (IpIf, Ip, Netmask);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        Status = EFI_DEVICE_ERROR;\r
+        Ip4FreeInterface (IpIf, IpInstance);\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      NetListInsertTail (&IpSb->Interfaces, &IpIf->Link);\r
+    }\r
+\r
+    //\r
+    // Add a route to this connected network in the route table\r
+    //\r
+    Ip4AddRoute (IpInstance->RouteTable, Ip, Netmask, IP4_ALLZERO_ADDRESS);\r
+\r
+  } else {\r
+    //\r
+    // Use the default address. If the default configuration hasn't\r
+    // been started, start it.\r
+    //\r
+    if (IpSb->State == IP4_SERVICE_UNSTARTED) {\r
+      Status = Ip4StartAutoConfig (IpSb);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_ERROR;\r
+      }\r
+    }\r
+\r
+    IpIf = IpSb->DefaultInterface;\r
+    NET_GET_REF (IpSb->DefaultInterface);\r
+\r
+    //\r
+    // If default address is used, so is the default route table.\r
+    // Any route set by the instance has the precedence over the\r
+    // routes in the default route table. Link the default table\r
+    // after the instance's table. Routing will search the local\r
+    // table first.\r
+    //\r
+    NET_GET_REF (IpSb->DefaultRouteTable);\r
+    IpInstance->RouteTable->Next = IpSb->DefaultRouteTable;\r
+  }\r
+\r
+  IpInstance->Interface = IpIf;\r
+  NetListInsertTail (&IpIf->IpInstances, &IpInstance->AddrLink);\r
+\r
+  CopyMem (&IpInstance->ConfigData, Config, sizeof (EFI_IP4_CONFIG_DATA));\r
+  IpInstance->State       = IP4_STATE_CONFIGED;\r
+\r
+  //\r
+  // Although EFI_NO_MAPPING is an error code, the IP child has been\r
+  // successfully configured and doesn't need reconfiguration when\r
+  // default address is acquired.\r
+  //\r
+  if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {\r
+    return EFI_NO_MAPPING;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  Ip4FreeRouteTable (IpInstance->RouteTable);\r
+  IpInstance->RouteTable = NULL;\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Clean up the IP4 child, release all the resources used by it.\r
+\r
+  @param  IpInstance             The IP4 child to clean up.\r
+\r
+  @retval EFI_SUCCESS            The IP4 child is cleaned up\r
+  @retval EFI_DEVICE_ERROR       Some resources failed to be released\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4CleanProtocol (\r
+  IN  IP4_PROTOCOL          *IpInstance\r
+  )\r
+{\r
+  if (EFI_ERROR (Ip4Cancel (IpInstance, NULL))) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (EFI_ERROR (Ip4Groups (IpInstance, FALSE, NULL))) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Some packets haven't been recycled. It is because either the\r
+  // user forgets to recycle the packets, or because the callback\r
+  // hasn't been called. Just leave it alone.\r
+  //\r
+  if (!NetListIsEmpty (&IpInstance->Delivered)) {\r
+    ;\r
+  }\r
+\r
+  if (IpInstance->Interface != NULL) {\r
+    NetListRemoveEntry (&IpInstance->AddrLink);\r
+    Ip4FreeInterface (IpInstance->Interface, IpInstance);\r
+    IpInstance->Interface = NULL;\r
+  }\r
+\r
+  if (IpInstance->RouteTable != NULL) {\r
+    if (IpInstance->RouteTable->Next != NULL) {\r
+      Ip4FreeRouteTable (IpInstance->RouteTable->Next);\r
+    }\r
+\r
+    Ip4FreeRouteTable (IpInstance->RouteTable);\r
+    IpInstance->RouteTable = NULL;\r
+  }\r
+\r
+  if (IpInstance->EfiRouteTable != NULL) {\r
+    NetFreePool (IpInstance->EfiRouteTable);\r
+    IpInstance->EfiRouteTable = NULL;\r
+    IpInstance->EfiRouteCount = 0;\r
+  }\r
+\r
+  if (IpInstance->Groups != NULL) {\r
+    NetFreePool (IpInstance->Groups);\r
+    IpInstance->Groups      = NULL;\r
+    IpInstance->GroupCount  = 0;\r
+  }\r
+\r
+  NetMapClean (&IpInstance->TxTokens);\r
+\r
+  NetMapClean (&IpInstance->RxTokens);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Validate that Ip/Netmask pair is OK to be used as station\r
+  address. Only continuous netmasks are supported. and check\r
+  that StationAddress is a unicast address on the newtwork.\r
+\r
+  @param  Ip                     The IP address to validate\r
+  @param  Netmask                The netmaks of the IP\r
+\r
+  @retval TRUE                   The Ip/Netmask pair is valid\r
+  @retval FALSE                  The\r
+\r
+**/\r
+BOOLEAN\r
+Ip4StationAddressValid (\r
+  IN IP4_ADDR               Ip,\r
+  IN IP4_ADDR               Netmask\r
+  )\r
+{\r
+  IP4_ADDR                  NetBrdcastMask;\r
+  INTN                      Len;\r
+  INTN                      Type;\r
+\r
+  //\r
+  // Only support the station address with 0.0.0.0/0 to enable DHCP client.\r
+  //\r
+  if (Netmask == IP4_ALLZERO_ADDRESS) {\r
+    return (BOOLEAN) (Ip == IP4_ALLZERO_ADDRESS);\r
+  }\r
+\r
+  //\r
+  // Only support the continuous net masks\r
+  //\r
+  if ((Len = NetGetMaskLength (Netmask)) == IP4_MASK_NUM) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Station address can't be class D or class E address\r
+  //\r
+  if ((Type = NetGetIpClass (Ip)) > IP4_ADDR_CLASSC) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Station address can't be subnet broadcast/net broadcast address\r
+  //\r
+  if ((Ip == (Ip & Netmask)) || (Ip == (Ip | ~Netmask))) {\r
+    return FALSE;\r
+  }\r
+\r
+  NetBrdcastMask = mIp4AllMasks[NET_MIN (Len, Type << 3)];\r
+\r
+  if (Ip == (Ip | ~NetBrdcastMask)) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Configure the EFI_IP4_PROTOCOL instance. If IpConfigData is NULL,\r
+  the instance is cleaned up. If the instance hasn't been configure\r
+  before, it will be initialized. Otherwise, the filter setting of\r
+  the instance is updated.\r
+\r
+  @param  This                   The IP4 child to configure\r
+  @param  IpConfigData           The configuration to apply. If NULL, clean it up.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid\r
+  @retval EFI_NO_MAPPING         The default address hasn't been configured and the\r
+                                 instance wants to use it.\r
+  @retval EFI_SUCCESS            The instance is configured.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp4Configure (\r
+  IN EFI_IP4_PROTOCOL       *This,\r
+  IN EFI_IP4_CONFIG_DATA    *IpConfigData       OPTIONAL\r
+  )\r
+{\r
+  IP4_PROTOCOL              *IpInstance;\r
+  EFI_IP4_CONFIG_DATA       *Current;\r
+  EFI_TPL                   OldTpl;\r
+  EFI_STATUS                Status;\r
+  BOOLEAN                   AddrOk;\r
+  IP4_ADDR                  IpAddress;\r
+  IP4_ADDR                  SubnetMask;\r
+\r
+  //\r
+  // First, validate the parameters\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);\r
+  OldTpl     = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  //\r
+  // Validate the configuration first.\r
+  //\r
+  if (IpConfigData != NULL) {\r
+    //\r
+    // This implementation doesn't support RawData\r
+    //\r
+    if (IpConfigData->RawData) {\r
+      Status = EFI_UNSUPPORTED;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+\r
+    NetCopyMem (&IpAddress, &IpConfigData->StationAddress, sizeof (IP4_ADDR));\r
+    NetCopyMem (&SubnetMask, &IpConfigData->SubnetMask, sizeof (IP4_ADDR));\r
+\r
+    IpAddress  = NTOHL (IpAddress);\r
+    SubnetMask = NTOHL (SubnetMask);\r
+\r
+    //\r
+    // Check whether the station address is a valid unicast address\r
+    //\r
+    if (!IpConfigData->UseDefaultAddress) {\r
+      AddrOk = Ip4StationAddressValid (IpAddress, SubnetMask);\r
+\r
+      if (!AddrOk) {\r
+        Status = EFI_INVALID_PARAMETER;\r
+        goto ON_EXIT;\r
+      }\r
+    }\r
+\r
+    //\r
+    // User can only update packet filters when already configured.\r
+    // If it wants to change the station address, it must configure(NULL)\r
+    // the instance first.\r
+    //\r
+    if (IpInstance->State == IP4_STATE_CONFIGED) {\r
+      Current = &IpInstance->ConfigData;\r
+\r
+      if (Current->UseDefaultAddress != IpConfigData->UseDefaultAddress) {\r
+        Status = EFI_ALREADY_STARTED;\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      if (!Current->UseDefaultAddress &&\r
+         (!EFI_IP4_EQUAL (Current->StationAddress, IpConfigData->StationAddress) ||\r
+          !EFI_IP4_EQUAL (Current->SubnetMask, IpConfigData->SubnetMask))) {\r
+        Status = EFI_ALREADY_STARTED;\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      if (Current->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {\r
+        return EFI_NO_MAPPING;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Configure the instance or clean it up.\r
+  //\r
+  if (IpConfigData != NULL) {\r
+    Status = Ip4ConfigProtocol (IpInstance, IpConfigData);\r
+  } else {\r
+    Status = Ip4CleanProtocol (IpInstance);\r
+\r
+    //\r
+    // Don't change the state if it is DESTORY, consider the following\r
+    // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped,\r
+    // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED,\r
+    // the unload fails miserably.\r
+    //\r
+    if (IpInstance->State == IP4_STATE_CONFIGED) {\r
+      IpInstance->State = IP4_STATE_UNCONFIGED;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Update the MNP's configure data. Ip4ServiceConfigMnp will check\r
+  // whether it is necessary to reconfigure the MNP.\r
+  //\r
+  Ip4ServiceConfigMnp (IpInstance->Service, FALSE);\r
+\r
+  //\r
+  // Update the variable data.\r
+  //\r
+  Ip4SetVariableData (IpInstance->Service);\r
+\r
+ON_EXIT:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+\r
+}\r
+\r
+\r
+/**\r
+  Change the IP4 child's multicast setting. The caller\r
+  should make sure that the parameters is valid.\r
+\r
+  @param  IpInstance             The IP4 child to change the setting.\r
+  @param  JoinFlag               TRUE to join the group, otherwise leave it\r
+  @param  GroupAddress           The target group address\r
+\r
+  @retval EFI_ALREADY_STARTED    Want to join the group, but already a member of it\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.\r
+  @retval EFI_DEVICE_ERROR       Failed to set the group configuraton\r
+  @retval EFI_SUCCESS            Successfully updated the group setting.\r
+  @retval EFI_NOT_FOUND          Try to leave the group which it isn't a member.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4Groups (\r
+  IN IP4_PROTOCOL           *IpInstance,\r
+  IN BOOLEAN                JoinFlag,\r
+  IN EFI_IPv4_ADDRESS       *GroupAddress       OPTIONAL\r
+  )\r
+{\r
+  IP4_ADDR                  *Members;\r
+  IP4_ADDR                  Group;\r
+  UINT32                    Index;\r
+\r
+  //\r
+  // Add it to the instance's Groups, and join the group by IGMP.\r
+  // IpInstance->Groups is in network byte order. IGMP operates in\r
+  // host byte order\r
+  //\r
+  if (JoinFlag) {\r
+    NetCopyMem (&Group, GroupAddress, sizeof (IP4_ADDR));\r
+\r
+    for (Index = 0; Index < IpInstance->GroupCount; Index++) {\r
+      if (IpInstance->Groups[Index] == Group) {\r
+        return EFI_ALREADY_STARTED;\r
+      }\r
+    }\r
+\r
+    Members = Ip4CombineGroups (IpInstance->Groups, IpInstance->GroupCount, Group);\r
+\r
+    if (Members == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    if (EFI_ERROR (Ip4JoinGroup (IpInstance, NTOHL (Group)))) {\r
+      NetFreePool (Members);\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    if (IpInstance->Groups != NULL) {\r
+      NetFreePool (IpInstance->Groups);\r
+    }\r
+\r
+    IpInstance->Groups = Members;\r
+    IpInstance->GroupCount++;\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Leave the group. Leave all the groups if GroupAddress is NULL.\r
+  // Must iterate from the end to the beginning because the GroupCount\r
+  // is decreamented each time an address is removed..\r
+  //\r
+  for (Index = IpInstance->GroupCount; Index > 0 ; Index--) {\r
+    Group = IpInstance->Groups[Index - 1];\r
+\r
+    if ((GroupAddress == NULL) || EFI_IP4_EQUAL (Group, *GroupAddress)) {\r
+      if (EFI_ERROR (Ip4LeaveGroup (IpInstance, NTOHL (Group)))) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      Ip4RemoveGroupAddr (IpInstance->Groups, IpInstance->GroupCount, Group);\r
+      IpInstance->GroupCount--;\r
+\r
+      if (IpInstance->GroupCount == 0) {\r
+        ASSERT (Index == 1);\r
+\r
+        NetFreePool (IpInstance->Groups);\r
+        IpInstance->Groups = NULL;\r
+      }\r
+\r
+      if (GroupAddress != NULL) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+  }\r
+\r
+  return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);\r
+}\r
+\r
+\r
+/**\r
+  Change the IP4 child's multicast setting. If JoinFlag is true,\r
+  the child wants to join the group. Otherwise it wants to leave\r
+  the group. If JoinFlag is false, and GroupAddress is NULL,\r
+  it will leave all the groups which is a member.\r
+\r
+  @param  This                   The IP4 child to change the setting.\r
+  @param  JoinFlag               TRUE to join the group, otherwise leave it.\r
+  @param  GroupAddress           The target group address\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameters are invalid\r
+  @retval EFI_SUCCESS            The group setting has been changed.\r
+  @retval Otherwise              It failed to change the setting.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp4Groups (\r
+  IN EFI_IP4_PROTOCOL       *This,\r
+  IN BOOLEAN                JoinFlag,\r
+  IN EFI_IPv4_ADDRESS       *GroupAddress     OPTIONAL\r
+  )\r
+{\r
+  IP4_PROTOCOL              *IpInstance;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  IP4_ADDR                  McastIp;\r
+\r
+  if ((This == NULL) || (JoinFlag && (GroupAddress == NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (GroupAddress != NULL) {\r
+    NetCopyMem (&McastIp, GroupAddress, sizeof (IP4_ADDR));\r
+\r
+    if (!IP4_IS_MULTICAST (NTOHL (McastIp))) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);\r
+  OldTpl     = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  if (IpInstance->State != IP4_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Ip4Groups (IpInstance, JoinFlag, GroupAddress);\r
+\r
+ON_EXIT:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Modify the IP child's route table. Each instance has its own\r
+  route table.\r
+\r
+  @param  This                   The IP4 child to modify the route\r
+  @param  DeleteRoute            TRUE to delete the route, otherwise add it\r
+  @param  SubnetAddress          The destination network\r
+  @param  SubnetMask             The destination network's mask\r
+  @param  GatewayAddress         The next hop address.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid.\r
+  @retval EFI_SUCCESS            The route table is successfully modified.\r
+  @retval Others                 Failed to modify the route table\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp4Routes (\r
+  IN EFI_IP4_PROTOCOL       *This,\r
+  IN BOOLEAN                DeleteRoute,\r
+  IN EFI_IPv4_ADDRESS       *SubnetAddress,\r
+  IN EFI_IPv4_ADDRESS       *SubnetMask,\r
+  IN EFI_IPv4_ADDRESS       *GatewayAddress\r
+  )\r
+{\r
+  IP4_PROTOCOL              *IpInstance;\r
+  IP4_INTERFACE             *IpIf;\r
+  IP4_ADDR                  Dest;\r
+  IP4_ADDR                  Netmask;\r
+  IP4_ADDR                  Nexthop;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  //\r
+  // First, validate the parameters\r
+  //\r
+  if ((This == NULL) || (SubnetAddress == NULL) ||\r
+      (SubnetMask == NULL) || (GatewayAddress == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);\r
+  OldTpl     = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  if (IpInstance->State != IP4_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  NetCopyMem (&Dest, SubnetAddress, sizeof (IP4_ADDR));\r
+  NetCopyMem (&Netmask, SubnetMask, sizeof (IP4_ADDR));\r
+  NetCopyMem (&Nexthop, GatewayAddress, sizeof (IP4_ADDR));\r
+\r
+  Dest    = NTOHL (Dest);\r
+  Netmask = NTOHL (Netmask);\r
+  Nexthop = NTOHL (Nexthop);\r
+\r
+  IpIf    = IpInstance->Interface;\r
+\r
+  if (!IP4_IS_VALID_NETMASK (Netmask)) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // the gateway address must be a unicast on the connected network if not zero.\r
+  //\r
+  if ((Nexthop != IP4_ALLZERO_ADDRESS) &&\r
+      (!IP4_NET_EQUAL (Nexthop, IpIf->Ip, IpIf->SubnetMask) ||\r
+        IP4_IS_BROADCAST (Ip4GetNetCast (Nexthop, IpIf)))) {\r
+\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (DeleteRoute) {\r
+    Status = Ip4DelRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop);\r
+  } else {\r
+    Status = Ip4AddRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop);\r
+  }\r
+\r
+ON_EXIT:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Check whether the user's token or event has already\r
+  been enqueue on IP4's list.\r
+\r
+  @param  Map                    The container of either user's transmit or receive\r
+                                 token.\r
+  @param  Item                   Current item to check against\r
+  @param  Context                The Token to check againist.\r
+\r
+  @retval EFI_ACCESS_DENIED      The token or event has already been enqueued in IP\r
+  @retval EFI_SUCCESS            The current item isn't the same token/event as the\r
+                                 context.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Ip4TokenExist (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_IP4_COMPLETION_TOKEN  *Token;\r
+  EFI_IP4_COMPLETION_TOKEN  *TokenInItem;\r
+\r
+  Token       = (EFI_IP4_COMPLETION_TOKEN *) Context;\r
+  TokenInItem = (EFI_IP4_COMPLETION_TOKEN *) Item->Key;\r
+\r
+  if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Validate the user's token against current station address.\r
+\r
+  @param  Token                  User's token to validate\r
+  @param  IpIf                   The IP4 child's interface.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Some parameters are invalid\r
+  @retval EFI_BAD_BUFFER_SIZE    The user's option/data is too long.\r
+  @retval EFI_SUCCESS            The token is OK\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Ip4TxTokenValid (\r
+  IN EFI_IP4_COMPLETION_TOKEN   *Token,\r
+  IN IP4_INTERFACE              *IpIf\r
+  )\r
+{\r
+  EFI_IP4_TRANSMIT_DATA     *TxData;\r
+  EFI_IP4_OVERRIDE_DATA     *Override;\r
+  IP4_ADDR                  Src;\r
+  IP4_ADDR                  Gateway;\r
+  UINT32                    Offset;\r
+  UINT32                    Index;\r
+  UINT32                    HeadLen;\r
+\r
+  if ((Token == NULL) || (Token->Event == NULL) || (Token->Packet.TxData == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  TxData = Token->Packet.TxData;\r
+\r
+  //\r
+  // Check the IP options: no more than 40 bytes and format is OK\r
+  //\r
+  if (TxData->OptionsLength != 0) {\r
+    if ((TxData->OptionsLength > 40) || (TxData->OptionsBuffer == NULL)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (!Ip4OptionIsValid (TxData->OptionsBuffer, TxData->OptionsLength, FALSE)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check the fragment table: no empty fragment, and length isn't bogus\r
+  //\r
+  if ((TxData->TotalDataLength == 0) || (TxData->FragmentCount == 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Offset = TxData->TotalDataLength;\r
+\r
+  for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
+    if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||\r
+        (TxData->FragmentTable[Index].FragmentLength == 0)) {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Offset -= TxData->FragmentTable[Index].FragmentLength;\r
+  }\r
+\r
+  if (Offset != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check the source and gateway: they must be a valid unicast.\r
+  // Gateway must also be on the connected network.\r
+  //\r
+  if (TxData->OverrideData) {\r
+    Override = TxData->OverrideData;\r
+\r
+    NetCopyMem (&Src, &Override->SourceAddress, sizeof (IP4_ADDR));\r
+    NetCopyMem (&Gateway, &Override->GatewayAddress, sizeof (IP4_ADDR));\r
+\r
+    Src     = NTOHL (Src);\r
+    Gateway = NTOHL (Gateway);\r
+\r
+    if ((NetGetIpClass (Src) > IP4_ADDR_CLASSC) ||\r
+        (Src == IP4_ALLONE_ADDRESS) ||\r
+        IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // If gateway isn't zero, it must be a unicast address, and\r
+    // on the connected network.\r
+    //\r
+    if ((Gateway != IP4_ALLZERO_ADDRESS) &&\r
+        ((NetGetIpClass (Gateway) > IP4_ADDR_CLASSC) ||\r
+         !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask) ||\r
+         IP4_IS_BROADCAST (Ip4GetNetCast (Gateway, IpIf)))) {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check the packet length: Head length and packet length all has a limit\r
+  //\r
+  HeadLen = sizeof (IP4_HEAD) + ((TxData->OptionsLength + 3) &~0x03);\r
+\r
+  if ((HeadLen > IP4_MAX_HEADLEN) ||\r
+      (TxData->TotalDataLength + HeadLen > IP4_MAX_PACKET_SIZE)) {\r
+\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  The callback function for the net buffer which wraps the user's\r
+  transmit token. Although it seems this function is pretty simple,\r
+  there are some subtle things.\r
+  When user requests the IP to transmit a packet by passing it a\r
+  token, the token is wrapped in an IP4_TXTOKEN_WRAP and the data\r
+  is wrapped in an net buffer. the net buffer's Free function is\r
+  set to Ip4FreeTxToken. The Token and token wrap are added to the\r
+  IP child's TxToken map. Then the buffer is passed to Ip4Output for\r
+  transmission. If something error happened before that, the buffer\r
+  is freed, which in turn will free the token wrap. The wrap may\r
+  have been added to the TxToken map or not, and the user's event\r
+  shouldn't be fired because we are still in the EfiIp4Transmit. If\r
+  the buffer has been sent by Ip4Output, it should be removed from\r
+  the TxToken map and user's event signaled. The token wrap and buffer\r
+  are bound together. Check the comments in Ip4Output for information\r
+  about IP fragmentation.\r
+\r
+  @param  Context                The token's wrap\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4FreeTxToken (\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP4_TXTOKEN_WRAP          *Wrap;\r
+  NET_MAP_ITEM              *Item;\r
+\r
+  Wrap = (IP4_TXTOKEN_WRAP *) Context;\r
+\r
+  //\r
+  // Find the token in the instance's map. EfiIp4Transmit put the\r
+  // token to the map. If that failed, NetMapFindKey will return NULL.\r
+  //\r
+  Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token);\r
+\r
+  if (Item != NULL) {\r
+    NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL);\r
+  }\r
+\r
+  if (Wrap->Sent) {\r
+    gBS->SignalEvent (Wrap->Token->Event);\r
+  }\r
+\r
+  NetFreePool (Wrap);\r
+}\r
+\r
+\r
+/**\r
+  The callback function to Ip4Output to update the transmit status.\r
+\r
+  @param  Ip4Instance            The Ip4Instance that request the transmit.\r
+  @param  Packet                 The user's transmit request\r
+  @param  IoStatus               The result of the transmission\r
+  @param  Flag                   Not used during transmission\r
+  @param  Context                The token's wrap.\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4OnPacketSent (\r
+  IP4_PROTOCOL              *Ip4Instance,\r
+  NET_BUF                   *Packet,\r
+  EFI_STATUS                IoStatus,\r
+  UINT32                    Flag,\r
+  VOID                      *Context\r
+  )\r
+{\r
+  IP4_TXTOKEN_WRAP          *Wrap;\r
+\r
+  //\r
+  // This is the transmission request from upper layer,\r
+  // not the IP4 driver itself.\r
+  //\r
+  ASSERT (Ip4Instance != NULL);\r
+\r
+  //\r
+  // The first fragment of the packet has been sent. Update\r
+  // the token's status. That is, if fragmented, the transmit's\r
+  // status is the first fragment's status. The Wrap will be\r
+  // release when all the fragments are release. Check the comments\r
+  // in Ip4FreeTxToken and Ip4Output for information.\r
+  //\r
+  Wrap                = (IP4_TXTOKEN_WRAP *) Context;\r
+  Wrap->Token->Status = IoStatus;\r
+\r
+  NetbufFree (Wrap->Packet);\r
+}\r
+\r
+\r
+/**\r
+  Transmit the user's data asynchronously. When transmission\r
+  completed,the Token's status is updated and its event signalled.\r
+\r
+  @param  This                   The IP4 child instance\r
+  @param  Token                  The user's transmit token, which contains user's\r
+                                 data, the result and an event to signal when\r
+                                 completed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid.\r
+  @retval EFI_NOT_STARTED        The IP4 child hasn't been started.\r
+  @retval EFI_SUCCESS            The user's data has been successfully enqueued\r
+                                 for transmission.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp4Transmit (\r
+  IN EFI_IP4_PROTOCOL         *This,\r
+  IN EFI_IP4_COMPLETION_TOKEN *Token\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+  IP4_PROTOCOL              *IpInstance;\r
+  IP4_INTERFACE             *IpIf;\r
+  IP4_TXTOKEN_WRAP          *Wrap;\r
+  EFI_IP4_TRANSMIT_DATA     *TxData;\r
+  EFI_IP4_CONFIG_DATA       *Config;\r
+  EFI_IP4_OVERRIDE_DATA     *Override;\r
+  IP4_HEAD                  Head;\r
+  IP4_ADDR                  GateWay;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  BOOLEAN                   DontFragment;\r
+  UINT32                    HeadLen;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);\r
+\r
+  if (IpInstance->State != IP4_STATE_CONFIGED) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  OldTpl  = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  IpSb    = IpInstance->Service;\r
+  IpIf    = IpInstance->Interface;\r
+  Config  = &IpInstance->ConfigData;\r
+\r
+  if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // make sure that token is properly formated\r
+  //\r
+  Status = Ip4TxTokenValid (Token, IpIf);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check whether the token or signal already existed.\r
+  //\r
+  if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip4TokenExist, Token))) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Build the IP header, need to fill in the Tos, TotalLen, Id,\r
+  // fragment, Ttl, protocol, Src, and Dst.\r
+  //\r
+  TxData = Token->Packet.TxData;\r
+\r
+  NetCopyMem (&Head.Dst, &TxData->DestinationAddress, sizeof (IP4_ADDR));\r
+  Head.Dst = NTOHL (Head.Dst);\r
+\r
+  if (TxData->OverrideData) {\r
+    Override      = TxData->OverrideData;\r
+    Head.Protocol = Override->Protocol;\r
+    Head.Tos      = Override->TypeOfService;\r
+    Head.Ttl      = Override->TimeToLive;\r
+    DontFragment  = Override->DoNotFragment;\r
+\r
+    NetCopyMem (&Head.Src, &Override->SourceAddress, sizeof (IP4_ADDR));\r
+    NetCopyMem (&GateWay, &Override->GatewayAddress, sizeof (IP4_ADDR));\r
+\r
+    Head.Src = NTOHL (Head.Src);\r
+    GateWay  = NTOHL (GateWay);\r
+  } else {\r
+    Head.Src      = IpIf->Ip;\r
+    GateWay       = IP4_ALLZERO_ADDRESS;\r
+    Head.Protocol = Config->DefaultProtocol;\r
+    Head.Tos      = Config->TypeOfService;\r
+    Head.Ttl      = Config->TimeToLive;\r
+    DontFragment  = Config->DoNotFragment;\r
+  }\r
+\r
+  Head.Fragment = IP4_HEAD_FRAGMENT_FIELD (DontFragment, FALSE, 0);\r
+  HeadLen       = sizeof (IP4_HEAD) + ((TxData->OptionsLength + 3) &~0x03);\r
+\r
+  //\r
+  // If don't fragment and fragment needed, return error\r
+  //\r
+  if (DontFragment && (TxData->TotalDataLength + HeadLen > IpSb->SnpMode.MaxPacketSize)) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // OK, it survives all the validation check. Wrap the token in\r
+  // a IP4_TXTOKEN_WRAP and the data in a netbuf\r
+  //\r
+  Status = EFI_OUT_OF_RESOURCES;\r
+  Wrap   = NetAllocatePool (sizeof (IP4_TXTOKEN_WRAP));\r
+  if (Wrap == NULL) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Wrap->IpInstance  = IpInstance;\r
+  Wrap->Token       = Token;\r
+  Wrap->Sent        = FALSE;\r
+  Wrap->Life        = IP4_US_TO_SEC (Config->TransmitTimeout);\r
+  Wrap->Packet      = NetbufFromExt (\r
+                        (NET_FRAGMENT *) TxData->FragmentTable,\r
+                        TxData->FragmentCount,\r
+                        IP4_MAX_HEADLEN,\r
+                        0,\r
+                        Ip4FreeTxToken,\r
+                        Wrap\r
+                        );\r
+\r
+  if (Wrap->Packet == NULL) {\r
+    NetFreePool (Wrap);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Token->Status = EFI_NOT_READY;\r
+\r
+  if (EFI_ERROR (NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap))) {\r
+    //\r
+    // NetbufFree will call Ip4FreeTxToken, which in turn will\r
+    // free the IP4_TXTOKEN_WRAP. Now, the token wrap hasn't been\r
+    // enqueued.\r
+    //\r
+    NetbufFree (Wrap->Packet);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Ip4Output (\r
+             IpSb,\r
+             IpInstance,\r
+             Wrap->Packet,\r
+             &Head,\r
+             TxData->OptionsBuffer,\r
+             TxData->OptionsLength,\r
+             GateWay,\r
+             Ip4OnPacketSent,\r
+             Wrap\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetbufFree (Wrap->Packet);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Mark the packet sent, so when Ip4FreeTxToken is called, it\r
+  // will signal the upper layer.\r
+  //\r
+  Wrap->Sent = TRUE;\r
+\r
+ON_EXIT:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Receive a packet for the upper layer. If there are packets\r
+  pending on the child's receive queue, the receive request\r
+  will be fulfilled immediately. Otherwise, the request is\r
+  enqueued. When receive request is completed, the status in\r
+  the Token is updated and its event is signalled.\r
+\r
+  @param  This                   The IP4 child to receive packet.\r
+  @param  Token                  The user's receive token\r
+\r
+  @retval EFI_INVALID_PARAMETER  The token is invalid.\r
+  @retval EFI_NOT_STARTED        The IP4 child hasn't been started\r
+  @retval EFI_ACCESS_DENIED      The token or event is already queued.\r
+  @retval EFI_SUCCESS            The receive request has been issued.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp4Receive (\r
+  IN EFI_IP4_PROTOCOL         *This,\r
+  IN EFI_IP4_COMPLETION_TOKEN *Token\r
+  )\r
+{\r
+  IP4_PROTOCOL              *IpInstance;\r
+  EFI_IP4_CONFIG_DATA       *Config;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  //\r
+  // First validate the parameters\r
+  //\r
+  if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  if (IpInstance->State != IP4_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Config = &IpInstance->ConfigData;\r
+\r
+  //\r
+  // Current Udp implementation creates an IP child for each Udp child.\r
+  // It initates a asynchronous receive immediately no matter whether\r
+  // there is no mapping or not. Disable this for now.\r
+  //\r
+#if 0\r
+  if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto ON_EXIT;\r
+  }\r
+#endif\r
+\r
+  //\r
+  // Check whether the toke is already on the receive queue.\r
+  //\r
+  Status = NetMapIterate (&IpInstance->RxTokens, Ip4TokenExist, Token);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Queue the token then check whether there is pending received packet.\r
+  //\r
+  Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Ip4InstanceDeliverPacket (IpInstance);\r
+\r
+ON_EXIT:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Cancel the transmitted but not recycled packet. If a matching\r
+  token is found, it will call Ip4CancelPacket to cancel the\r
+  packet. Ip4CancelPacket will cancel all the fragments of the\r
+  packet. When all the fragments are freed, the IP4_TXTOKEN_WRAP\r
+  will be deleted from the Map, and user's event signalled.\r
+  Because Ip4CancelPacket and other functions are all called in\r
+  line, so, after Ip4CancelPacket returns, the Item has been freed.\r
+\r
+  @param  Map                    The IP4 child's transmit queue\r
+  @param  Item                   The current transmitted packet to test.\r
+  @param  Context                The user's token to cancel.\r
+\r
+  @retval EFI_SUCCESS            Continue to check the next Item.\r
+  @retval EFI_ABORTED            The user's Token (Token != NULL) is cancelled.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Ip4CancelTxTokens (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_IP4_COMPLETION_TOKEN  *Token;\r
+  IP4_TXTOKEN_WRAP          *Wrap;\r
+\r
+  Token = (EFI_IP4_COMPLETION_TOKEN *) Context;\r
+\r
+  //\r
+  // Return EFI_SUCCESS to check the next item in the map if\r
+  // this one doesn't match.\r
+  //\r
+  if ((Token != NULL) && (Token != Item->Key)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Wrap = (IP4_TXTOKEN_WRAP *) Item->Value;\r
+  ASSERT (Wrap != NULL);\r
+\r
+  //\r
+  // Don't access the Item, Wrap and Token's members after this point.\r
+  // Item and wrap has been freed. And we no longer own the Token.\r
+  //\r
+  Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);\r
+\r
+  //\r
+  // If only one item is to be cancel, return EFI_ABORTED to stop\r
+  // iterating the map any more.\r
+  //\r
+  if (Token != NULL) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cancel the receive request. This is quiet simple, because\r
+  it is only enqueued in our local receive map.\r
+\r
+  @param  Map                    The IP4 child's receive queue\r
+  @param  Item                   Current receive request to cancel.\r
+  @param  Context                The user's token to cancel\r
+\r
+  @retval EFI_SUCCESS            Continue to check the next receive request on the\r
+                                 queue.\r
+  @retval EFI_ABORTED            The user's token (token != NULL) has been\r
+                                 cancelled.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Ip4CancelRxTokens (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_IP4_COMPLETION_TOKEN  *Token;\r
+  EFI_IP4_COMPLETION_TOKEN  *This;\r
+\r
+  Token = (EFI_IP4_COMPLETION_TOKEN *) Context;\r
+  This  = Item->Key;\r
+\r
+  if ((Token != NULL) && (Token != This)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  NetMapRemoveItem (Map, Item, NULL);\r
+\r
+  This->Status        = EFI_ABORTED;\r
+  This->Packet.RxData = NULL;\r
+  gBS->SignalEvent (This->Event);\r
+\r
+  if (Token != NULL) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cancel the user's receive/transmit request.\r
+\r
+  @param  IpInstance             The IP4 child\r
+  @param  Token                  The token to cancel. If NULL, all token will be\r
+                                 cancelled.\r
+\r
+  @retval EFI_SUCCESS            The token is cancelled\r
+  @retval EFI_NOT_FOUND          The token isn't found on either the\r
+                                 transmit/receive queue\r
+  @retval EFI_DEVICE_ERROR       Not all token is cancelled when Token is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4Cancel (\r
+  IN IP4_PROTOCOL             *IpInstance,\r
+  IN EFI_IP4_COMPLETION_TOKEN *Token          OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // First check the transmitted packet. Ip4CancelTxTokens returns\r
+  // EFI_ABORTED to mean that the token has been cancelled when\r
+  // token != NULL. So, return EFI_SUCCESS for this condition.\r
+  //\r
+  Status = NetMapIterate (&IpInstance->TxTokens, Ip4CancelTxTokens, Token);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Check the receive queue. Ip4CancelRxTokens also returns EFI_ABORT\r
+  // for Token!=NULL and it is cancelled.\r
+  //\r
+  Status = NetMapIterate (&IpInstance->RxTokens, Ip4CancelRxTokens, Token);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // OK, if the Token is found when Token != NULL, the NetMapIterate\r
+  // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS.\r
+  //\r
+  if (Token != NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // If Token == NULL, cancel all the tokens. return error if no\r
+  // all of them are cancelled.\r
+  //\r
+  if (!NetMapIsEmpty (&IpInstance->TxTokens) ||\r
+      !NetMapIsEmpty (&IpInstance->RxTokens)) {\r
+\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cancel the queued receive/transmit requests. If Token is NULL,\r
+  all the queued requests will be cancelled. It just validate\r
+  the parameter then pass them to Ip4Cancel.\r
+\r
+  @param  This                   The IP4 child to cancel the request\r
+  @param  Token                  The token to cancel, if NULL, cancel all.\r
+\r
+  @retval EFI_INVALID_PARAMETER  This is NULL\r
+  @retval EFI_NOT_STARTED        The IP4 child hasn't been configured.\r
+  @retval EFI_NO_MAPPING         The IP4 child is configured to use the default,\r
+                                 but the default address hasn't been acquired.\r
+  @retval EFI_SUCCESS            The Token is cancelled.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp4Cancel (\r
+  IN EFI_IP4_PROTOCOL         *This,\r
+  IN EFI_IP4_COMPLETION_TOKEN *Token    OPTIONAL\r
+  )\r
+{\r
+  IP4_PROTOCOL              *IpInstance;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  if (IpInstance->State != IP4_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Ip4Cancel (IpInstance, Token);\r
+\r
+ON_EXIT:\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Poll the network stack. The EFI network stack is poll based. There\r
+  is no interrupt support for the network interface card.\r
+\r
+  @param  This                   The IP4 child to poll through\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid\r
+  @retval EFI_NOT_STARTED        The IP4 child hasn't been configured.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp4Poll (\r
+  IN EFI_IP4_PROTOCOL       *This\r
+  )\r
+{\r
+  IP4_PROTOCOL                  *IpInstance;\r
+  EFI_MANAGED_NETWORK_PROTOCOL  *Mnp;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This);\r
+\r
+  if (IpInstance->State == IP4_STATE_UNCONFIGED) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  Mnp = IpInstance->Service->Mnp;\r
+\r
+  //\r
+  // Don't lock the Poll function to enable the deliver of\r
+  // the packet polled up.\r
+  //\r
+  return Mnp->Poll (Mnp);\r
+}\r
+\r
+EFI_IP4_PROTOCOL\r
+mEfiIp4ProtocolTemplete = {\r
+  EfiIp4GetModeData,\r
+  EfiIp4Configure,\r
+  EfiIp4Groups,\r
+  EfiIp4Routes,\r
+  EfiIp4Transmit,\r
+  EfiIp4Receive,\r
+  EfiIp4Cancel,\r
+  EfiIp4Poll\r
+};\r
+\r
+\r
+/**\r
+  Decrease the life of the transmitted packets. If it is\r
+  decreased to zero, cancel the packet. This function is\r
+  called by Ip4packetTimerTicking which time out both the\r
+  received-but-not-delivered and transmitted-but-not-recycle\r
+  packets.\r
+\r
+  @param  Map                    The IP4 child's transmit map.\r
+  @param  Item                   Current transmitted packet\r
+  @param  Context                Not used.\r
+\r
+  @retval EFI_SUCCESS            Always returns EFI_SUCCESS\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4SentPacketTicking (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP4_TXTOKEN_WRAP          *Wrap;\r
+\r
+  Wrap = (IP4_TXTOKEN_WRAP *) Item->Value;\r
+  ASSERT (Wrap != NULL);\r
+\r
+  if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {\r
+    Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  The heart beat timer of IP4 service instance. It times out\r
+  all of its IP4 children's received-but-not-delivered and\r
+  transmitted-but-not-recycle packets, and provides time input\r
+  for its IGMP protocol.\r
+\r
+  @param  Event                  The IP4 service instance's heart beat timer.\r
+  @param  Context                The IP4 service instance.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip4TimerTicking (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+\r
+  IpSb = (IP4_SERVICE *) Context;\r
+  NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE);\r
+\r
+  Ip4PacketTimerTicking (IpSb);\r
+  Ip4IgmpTicking (IpSb);\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h
new file mode 100644 (file)
index 0000000..1bbee93
--- /dev/null
@@ -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>\r
+\r
+#include <Protocol/IP4.h>\r
+#include <Protocol/IP4Config.h>\r
+#include <Protocol/Arp.h>\r
+#include <Protocol/ManagedNetwork.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/BaseLib.h>\r
+#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 (file)
index 0000000..f5c4c9e
--- /dev/null
@@ -0,0 +1,1235 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Ip4Input.c\r
+\r
+Abstract:\r
+\r
+  IP4 input process.\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+\r
+/**\r
+  Create a empty assemble entry for the packet identified by\r
+  (Dst, Src, Id, Protocol). The default life for the packet is\r
+  120 seconds.\r
+\r
+  @param  Dst                    The destination address\r
+  @param  Src                    The source address\r
+  @param  Id                     The ID field in IP header\r
+  @param  Protocol               The protocol field in IP header\r
+\r
+  @return NULL if failed to allocate memory for the entry, otherwise\r
+  @return the point to just created reassemble entry.\r
+\r
+**/\r
+STATIC\r
+IP4_ASSEMBLE_ENTRY *\r
+Ip4CreateAssembleEntry (\r
+  IN IP4_ADDR               Dst,\r
+  IN IP4_ADDR               Src,\r
+  IN UINT16                 Id,\r
+  IN UINT8                  Protocol\r
+  )\r
+{\r
+\r
+  IP4_ASSEMBLE_ENTRY        *Assemble;\r
+\r
+  Assemble = NetAllocatePool (sizeof (IP4_ASSEMBLE_ENTRY));\r
+\r
+  if (Assemble == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NetListInit (&Assemble->Link);\r
+  NetListInit (&Assemble->Fragments);\r
+\r
+  Assemble->Dst      = Dst;\r
+  Assemble->Src      = Src;\r
+  Assemble->Id       = Id;\r
+  Assemble->Protocol = Protocol;\r
+  Assemble->TotalLen = 0;\r
+  Assemble->CurLen   = 0;\r
+  Assemble->Head     = NULL;\r
+  Assemble->Info     = NULL;\r
+  Assemble->Life     = IP4_FRAGMENT_LIFE;\r
+\r
+  return Assemble;\r
+}\r
+\r
+\r
+/**\r
+  Release all the fragments of a packet, then free the assemble entry\r
+\r
+  @param  Assemble               The assemble entry to free\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4FreeAssembleEntry (\r
+  IN IP4_ASSEMBLE_ENTRY     *Assemble\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  NET_BUF                   *Fragment;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {\r
+    Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+\r
+    NetListRemoveEntry (Entry);\r
+    NetbufFree (Fragment);\r
+  }\r
+\r
+  NetFreePool (Assemble);\r
+}\r
+\r
+\r
+/**\r
+  Initialize an already allocated assemble table. This is generally\r
+  the assemble table embedded in the IP4 service instance.\r
+\r
+  @param  Table                  The assemble table to initialize.\r
+\r
+  @return NONE\r
+\r
+**/\r
+VOID\r
+Ip4InitAssembleTable (\r
+  IN IP4_ASSEMBLE_TABLE     *Table\r
+  )\r
+{\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {\r
+    NetListInit (&Table->Bucket[Index]);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Clean up the assemble table: remove all the fragments\r
+  and assemble entries.\r
+\r
+  @param  Table                  The assemble table to clean up\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4CleanAssembleTable (\r
+  IN IP4_ASSEMBLE_TABLE     *Table\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_ASSEMBLE_ENTRY        *Assemble;\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {\r
+      Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);\r
+\r
+      NetListRemoveEntry (Entry);\r
+      Ip4FreeAssembleEntry (Assemble);\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Trim the packet to fit in [Start, End), and update the per\r
+  packet information.\r
+\r
+  @param  Packet                 Packet to trim\r
+  @param  Start                  The sequence of the first byte to fit in\r
+  @param  End                    One beyond the sequence of last byte to fit in.\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4TrimPacket (\r
+  IN NET_BUF                *Packet,\r
+  IN INTN                   Start,\r
+  IN INTN                   End\r
+  )\r
+{\r
+  IP4_CLIP_INFO             *Info;\r
+  INTN                      Len;\r
+\r
+  Info = IP4_GET_CLIP_INFO (Packet);\r
+\r
+  ASSERT (Info->Start + Info->Length == Info->End);\r
+  ASSERT ((Info->Start < End) && (Start < Info->End));\r
+\r
+   if (Info->Start < Start) {\r
+    Len = Start - Info->Start;\r
+\r
+    NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);\r
+    Info->Start   = Start;\r
+    Info->Length -= Len;\r
+  }\r
+\r
+  if (End < Info->End) {\r
+    Len = End - Info->End;\r
+\r
+    NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);\r
+    Info->End     = End;\r
+    Info->Length -= Len;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Release all the fragments of the packet. This is the callback for\r
+  the assembled packet's OnFree. It will free the assemble entry,\r
+  which in turn will free all the fragments of the packet.\r
+\r
+  @param  Arg                    The assemble entry to free\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4OnFreeFragments (\r
+  IN VOID                   *Arg\r
+  )\r
+{\r
+  Ip4FreeAssembleEntry ((IP4_ASSEMBLE_ENTRY *) Arg);\r
+}\r
+\r
+\r
+/**\r
+  Reassemble the IP fragments. If all the fragments of the packet\r
+  have been received, it will wrap the packet in a net buffer then\r
+  return it to caller. If the packet can't be assembled, NULL is\r
+  return.\r
+\r
+  @param  Table                  The assemble table used.\r
+  @param  Packet                 The fragment to assemble\r
+\r
+  @return NULL if the packet can't be reassemble. The point to just assembled\r
+  @return packet if all the fragments of the packet have arrived.\r
+\r
+**/\r
+STATIC\r
+NET_BUF *\r
+Ip4Reassemble (\r
+  IN IP4_ASSEMBLE_TABLE     *Table,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP4_HEAD                  *IpHead;\r
+  IP4_CLIP_INFO             *This;\r
+  IP4_CLIP_INFO             *Node;\r
+  IP4_ASSEMBLE_ENTRY        *Assemble;\r
+  NET_LIST_ENTRY            *Head;\r
+  NET_LIST_ENTRY            *Prev;\r
+  NET_LIST_ENTRY            *Cur;\r
+  NET_BUF                   *Fragment;\r
+  NET_BUF                   *NewPacket;\r
+  INTN                      Index;\r
+\r
+  IpHead  = Packet->Ip;\r
+  This    = IP4_GET_CLIP_INFO (Packet);\r
+\r
+  ASSERT (IpHead != NULL);\r
+\r
+  //\r
+  // First: find the related assemble entry\r
+  //\r
+  Assemble  = NULL;\r
+  Index     = IP4_ASSEMBLE_HASH (IpHead->Dst, IpHead->Src, IpHead->Id, IpHead->Protocol);\r
+\r
+  NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {\r
+    Assemble = NET_LIST_USER_STRUCT (Cur, IP4_ASSEMBLE_ENTRY, Link);\r
+\r
+    if ((Assemble->Dst == IpHead->Dst) && (Assemble->Src == IpHead->Src) &&\r
+        (Assemble->Id == IpHead->Id)   && (Assemble->Protocol == IpHead->Protocol)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Create a new assemble entry if no assemble entry is related to this packet\r
+  //\r
+  if (Cur == &Table->Bucket[Index]) {\r
+    Assemble = Ip4CreateAssembleEntry (\r
+                 IpHead->Dst,\r
+                 IpHead->Src,\r
+                 IpHead->Id,\r
+                 IpHead->Protocol\r
+                 );\r
+\r
+    if (Assemble == NULL) {\r
+      goto DROP;\r
+    }\r
+\r
+    NetListInsertHead (&Table->Bucket[Index], &Assemble->Link);\r
+  }\r
+\r
+  //\r
+  // Find the point to insert the packet: before the first\r
+  // fragment with THIS.Start < CUR.Start. the previous one\r
+  // has PREV.Start <= THIS.Start < CUR.Start.\r
+  //\r
+  Head = &Assemble->Fragments;\r
+\r
+  NET_LIST_FOR_EACH (Cur, Head) {\r
+    Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+\r
+    if (This->Start < IP4_GET_CLIP_INFO (Fragment)->Start) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check whether the current fragment overlaps with the previous one.\r
+  // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to\r
+  // check whether THIS.Start < PREV.End for overlap. If two fragments\r
+  // overlaps, trim the overlapped part off THIS fragment.\r
+  //\r
+  if ((Prev = Cur->ForwardLink) != Head) {\r
+    Fragment  = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);\r
+    Node      = IP4_GET_CLIP_INFO (Fragment);\r
+\r
+    if (This->Start < Node->End) {\r
+      if (This->End <= Node->End) {\r
+        NetbufFree (Packet);\r
+        return NULL;\r
+      }\r
+\r
+      Ip4TrimPacket (Packet, Node->End, This->End);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Insert the fragment into the packet. The fragment may be removed\r
+  // from the list by the following checks.\r
+  //\r
+  NetListInsertBefore (Cur, &Packet->List);\r
+\r
+  //\r
+  // Check the packets after the insert point. It holds that:\r
+  // THIS.Start <= NODE.Start < NODE.End. The equality holds\r
+  // if PREV and NEXT are continuous. THIS fragment may fill\r
+  // several holes. Remove the completely overlapped fragments\r
+  //\r
+  while (Cur != Head) {\r
+    Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+    Node     = IP4_GET_CLIP_INFO (Fragment);\r
+\r
+    //\r
+    // Remove fragments completely overlapped by this fragment\r
+    //\r
+    if (Node->End <= This->End) {\r
+      Cur = Cur->ForwardLink;\r
+\r
+      NetListRemoveEntry (&Fragment->List);\r
+      Assemble->CurLen -= Node->Length;\r
+\r
+      NetbufFree (Fragment);\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // The conditions are: THIS.Start <= NODE.Start, and THIS.End <\r
+    // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.\r
+    // If two fragments start at the same offset, remove THIS fragment\r
+    // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).\r
+    //\r
+    if (Node->Start < This->End) {\r
+      if (This->Start == Node->Start) {\r
+        NetListRemoveEntry (&Packet->List);\r
+        goto DROP;\r
+      }\r
+\r
+      Ip4TrimPacket (Packet, This->Start, Node->Start);\r
+    }\r
+\r
+    break;\r
+  }\r
+\r
+  //\r
+  // Update the assemble info: increase the current length. If it is\r
+  // the frist fragment, update the packet's IP head and per packet\r
+  // info. If it is the last fragment, update the total length.\r
+  //\r
+  Assemble->CurLen += This->Length;\r
+\r
+  if (This->Start == 0) {\r
+    //\r
+    // Once the first fragment is enqueued, it can't be removed\r
+    // from the fragment list. So, Assemble->Head always point\r
+    // to valid memory area.\r
+    //\r
+    ASSERT (Assemble->Head == NULL);\r
+\r
+    Assemble->Head  = IpHead;\r
+    Assemble->Info  = IP4_GET_CLIP_INFO (Packet);\r
+  }\r
+\r
+  //\r
+  // Don't update the length more than once.\r
+  //\r
+  if (IP4_LAST_FRAGMENT (IpHead->Fragment) && (Assemble->TotalLen == 0)) {\r
+    Assemble->TotalLen = This->End;\r
+  }\r
+\r
+  //\r
+  // Deliver the whole packet if all the fragments received.\r
+  // All fragments received if:\r
+  //  1. received the last one, so, the totoal length is know\r
+  //  2. received all the data. If the last fragment on the\r
+  //     queue ends at the total length, all data is received.\r
+  //\r
+  if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {\r
+\r
+    NetListRemoveEntry (&Assemble->Link);\r
+\r
+    //\r
+    // If the packet is properly formated, the last fragment's End\r
+    // equals to the packet's total length. Otherwise, the packet\r
+    // is a fake, drop it now.\r
+    //\r
+    Fragment = NET_LIST_USER_STRUCT (Head->BackLink, NET_BUF, List);\r
+\r
+    if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) {\r
+      Ip4FreeAssembleEntry (Assemble);\r
+      return NULL;\r
+    }\r
+\r
+    //\r
+    // Wrap the packet in a net buffer then deliver it up\r
+    //\r
+    NewPacket = NetbufFromBufList (\r
+                  &Assemble->Fragments,\r
+                  0,\r
+                  0,\r
+                  Ip4OnFreeFragments,\r
+                  Assemble\r
+                  );\r
+\r
+    if (NewPacket == NULL) {\r
+      Ip4FreeAssembleEntry (Assemble);\r
+      return NULL;\r
+    }\r
+\r
+    NewPacket->Ip                  = Assemble->Head;\r
+    CopyMem (IP4_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP4_CLIP_INFO));\r
+    return NewPacket;\r
+  }\r
+\r
+  return NULL;\r
+\r
+DROP:\r
+  NetbufFree (Packet);\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  The IP4 input routine. It is called by the IP4_INTERFACE when a\r
+  IP4 fragment is received from MNP.\r
+\r
+  @param  Ip4Instance            The IP4 child that request the receive, most like\r
+                                 it is NULL.\r
+  @param  Packet                 The IP4 packet received.\r
+  @param  IoStatus               The return status of receive request.\r
+  @param  Flag                   The link layer flag for the packet received, such\r
+                                 as multicast.\r
+  @param  Context                The IP4 service instance that own the MNP.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4AccpetFrame (\r
+  IN IP4_PROTOCOL           *Ip4Instance,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN UINT32                 Flag,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+  IP4_CLIP_INFO             *Info;\r
+  IP4_HEAD                  *Head;\r
+  UINT32                    HeadLen;\r
+  UINT32                    OptionLen;\r
+  UINT32                    TotalLen;\r
+  UINT16                    Checksum;\r
+\r
+  IpSb = (IP4_SERVICE *) Context;\r
+\r
+  if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTORY)) {\r
+    goto DROP;\r
+  }\r
+\r
+  //\r
+  // Check that the IP4 header is correctly formated\r
+  //\r
+  if (Packet->TotalSize < IP4_MIN_HEADLEN) {\r
+    goto RESTART;\r
+  }\r
+\r
+  Head     = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+  HeadLen  = (Head->HeadLen << 2);\r
+  TotalLen = NTOHS (Head->TotalLen);\r
+\r
+  //\r
+  // Mnp may deliver frame trailer sequence up, trim it off.\r
+  //\r
+  if (TotalLen < Packet->TotalSize) {\r
+    NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE);\r
+  }\r
+\r
+  if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) ||\r
+      (TotalLen < HeadLen) || (TotalLen != Packet->TotalSize)) {\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Some OS may send IP packets without checksum.\r
+  //\r
+  Checksum = ~NetblockChecksum ((UINT8 *) Head, HeadLen);\r
+\r
+  if ((Head->Checksum != 0) && (Checksum != 0)) {\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Convert the IP header to host byte order, then get the per packet info.\r
+  //\r
+  Packet->Ip      = Ip4NtohHead (Head);\r
+\r
+  Info            = IP4_GET_CLIP_INFO (Packet);\r
+  Info->LinkFlag  = Flag;\r
+  Info->CastType  = Ip4GetHostCast (IpSb, Head->Dst, Head->Src);\r
+  Info->Start     = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3;\r
+  Info->Length    = Head->TotalLen - HeadLen;\r
+  Info->End       = Info->Start + Info->Length;\r
+  Info->Status    = EFI_SUCCESS;\r
+\r
+  //\r
+  // The packet is destinated to us if the CastType is non-zero.\r
+  //\r
+  if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) {\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Validate the options. Don't call the Ip4OptionIsValid if\r
+  // there is no option to save some CPU process.\r
+  //\r
+  OptionLen = HeadLen - IP4_MIN_HEADLEN;\r
+\r
+  if ((OptionLen > 0) && !Ip4OptionIsValid ((UINT8 *) (Head + 1), OptionLen, TRUE)) {\r
+    goto RESTART;\r
+  }\r
+\r
+  //\r
+  // Trim the head off, after this point, the packet is headless.\r
+  // and Packet->TotalLen == Info->Length.\r
+  //\r
+  NetbufTrim (Packet, HeadLen, TRUE);\r
+\r
+  //\r
+  // Reassemble the packet if this is a fragment. The packet is a\r
+  // fragment if its head has MF (more fragment) set, or it starts\r
+  // at non-zero byte.\r
+  //\r
+  if ((Head->Fragment & IP4_HEAD_MF_MASK) || (Info->Start != 0)) {\r
+    //\r
+    // Drop the fragment if DF is set but it is fragmented. Gateway\r
+    // need to send a type 4 destination unreache ICMP message here.\r
+    //\r
+    if (Head->Fragment & IP4_HEAD_DF_MASK) {\r
+      goto RESTART;\r
+    }\r
+\r
+    //\r
+    // The length of all but the last fragments is in the unit of 8 bytes.\r
+    //\r
+    if ((Head->Fragment & IP4_HEAD_MF_MASK) && (Info->Length % 8 != 0)) {\r
+      goto RESTART;\r
+    }\r
+\r
+    Packet = Ip4Reassemble (&IpSb->Assemble, Packet);\r
+\r
+    //\r
+    // Packet assembly isn't complete, start receive more packet.\r
+    //\r
+    if (Packet == NULL) {\r
+      goto RESTART;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Packet may have been changed. Head, HeadLen, TotalLen, and\r
+  // info must be reloaded bofore use. The ownership of the packet\r
+  // is transfered to the packet process logic.\r
+  //\r
+  Head  = Packet->Ip;\r
+  IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;\r
+\r
+  switch (Head->Protocol) {\r
+  case IP4_PROTO_ICMP:\r
+    Ip4IcmpHandle (IpSb, Head, Packet);\r
+    break;\r
+\r
+  case IP4_PROTO_IGMP:\r
+    Ip4IgmpHandle (IpSb, Head, Packet);\r
+    break;\r
+\r
+  default:\r
+    Ip4Demultiplex (IpSb, Head, Packet);\r
+  }\r
+\r
+  Packet = NULL;\r
+\r
+RESTART:\r
+  Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);\r
+\r
+DROP:\r
+  if (Packet != NULL) {\r
+    NetbufFree (Packet);\r
+  }\r
+\r
+  return ;\r
+}\r
+\r
+\r
+/**\r
+  Check whether this IP child accepts the packet.\r
+\r
+  @param  IpInstance             The IP child to check\r
+  @param  Head                   The IP header of the packet\r
+  @param  Packet                 The data of the packet\r
+\r
+  @return TRUE if the child wants to receive the packet, otherwise return FALSE.\r
+\r
+**/\r
+BOOLEAN\r
+Ip4InstanceFrameAcceptable (\r
+  IN IP4_PROTOCOL           *IpInstance,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP4_ICMP_ERROR_HEAD       Icmp;\r
+  EFI_IP4_CONFIG_DATA       *Config;\r
+  IP4_CLIP_INFO             *Info;\r
+  UINT16                    Proto;\r
+  UINT32                    Index;\r
+\r
+  Config = &IpInstance->ConfigData;\r
+\r
+  //\r
+  // Dirty trick for the Tiano UEFI network stack implmentation. If\r
+  // ReceiveTimeout == -1, the receive of the packet for this instance\r
+  // is disabled. The UEFI spec don't have such captibility. We add\r
+  // this to improve the performance because IP will make a copy of\r
+  // the received packet for each accepting instance. Some IP instances\r
+  // used by UDP/TCP only send packets, they don't wants to receive.\r
+  //\r
+  if (Config->ReceiveTimeout == (UINT32)(-1)) {\r
+    return FALSE;\r
+  }\r
+\r
+  if (Config->AcceptPromiscuous) {\r
+    return TRUE;\r
+  }\r
+\r
+  //\r
+  // Use protocol from the IP header embedded in the ICMP error\r
+  // message to filter, instead of ICMP itself. ICMP handle will\r
+  // can Ip4Demultiplex to deliver ICMP errors.\r
+  //\r
+  Proto = Head->Protocol;\r
+\r
+  if (Proto == IP4_PROTO_ICMP) {\r
+    NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *) &Icmp.Head);\r
+\r
+    if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) {\r
+      if (!Config->AcceptIcmpErrors) {\r
+        return FALSE;\r
+      }\r
+\r
+      NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+      Proto = Icmp.IpHead.Protocol;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Match the protocol\r
+  //\r
+  if (!Config->AcceptAnyProtocol && (Proto != Config->DefaultProtocol)) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check for broadcast, the caller has computed the packet's\r
+  // cast type for this child's interface.\r
+  //\r
+  Info = IP4_GET_CLIP_INFO (Packet);\r
+\r
+  if (IP4_IS_BROADCAST (Info->CastType)) {\r
+    return Config->AcceptBroadcast;\r
+  }\r
+\r
+  //\r
+  // If it is a multicast packet, check whether we are in the group.\r
+  //\r
+  if (Info->CastType == IP4_MULTICAST) {\r
+    //\r
+    // Receive the multicast if the instance wants to receive all packets.\r
+    //\r
+    if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) {\r
+      return TRUE;\r
+    }\r
+\r
+    for (Index = 0; Index < IpInstance->GroupCount; Index++) {\r
+      if (IpInstance->Groups[Index] == HTONL (Head->Dst)) {\r
+        break;\r
+      }\r
+    }\r
+\r
+    return (BOOLEAN)(Index < IpInstance->GroupCount);\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Enqueue a shared copy of the packet to the IP4 child if the\r
+  packet is acceptable to it. Here the data of the packet is\r
+  shared, but the net buffer isn't.\r
+\r
+  @param  IpInstance             The IP4 child to enqueue the packet to\r
+  @param  Head                   The IP header of the received packet\r
+  @param  Packet                 The data of the received packet\r
+\r
+  @retval EFI_NOT_STARTED        The IP child hasn't been configured.\r
+  @retval EFI_INVALID_PARAMETER  The child doesn't want to receive the packet\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resource\r
+  @retval EFI_SUCCESS            A shared copy the packet is enqueued to the child.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4InstanceEnquePacket (\r
+  IN IP4_PROTOCOL           *IpInstance,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP4_CLIP_INFO             *Info;\r
+  NET_BUF                   *Clone;\r
+\r
+  //\r
+  // Check whether the packet is acceptable to this instance.\r
+  //\r
+  if (IpInstance->State != IP4_STATE_CONFIGED) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (!Ip4InstanceFrameAcceptable (IpInstance, Head, Packet)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Enque a shared copy of the packet.\r
+  //\r
+  Clone = NetbufClone (Packet);\r
+\r
+  if (Clone == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Set the receive time out for the assembled packet. If it expires,\r
+  // packet will be removed from the queue.\r
+  //\r
+  Info        = IP4_GET_CLIP_INFO (Clone);\r
+  Info->Life  = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);\r
+\r
+  NetListInsertTail (&IpInstance->Received, &Clone->List);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  The signal handle of IP4's recycle event. It is called back\r
+  when the upper layer release the packet.\r
+\r
+  @param  Event                  The IP4's recycle event.\r
+  @param  Context                The context of the handle, which is a\r
+                                 IP4_RXDATA_WRAP\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Ip4OnRecyclePacket (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP4_RXDATA_WRAP           *Wrap;\r
+\r
+  Wrap = (IP4_RXDATA_WRAP *) Context;\r
+\r
+  NET_TRYLOCK (&Wrap->IpInstance->RecycleLock);\r
+  NetListRemoveEntry (&Wrap->Link);\r
+  NET_UNLOCK (&Wrap->IpInstance->RecycleLock);\r
+\r
+  ASSERT (!NET_BUF_SHARED (Wrap->Packet));\r
+  NetbufFree (Wrap->Packet);\r
+\r
+  gBS->CloseEvent (Wrap->RxData.RecycleSignal);\r
+  NetFreePool (Wrap);\r
+}\r
+\r
+\r
+/**\r
+  Wrap the received packet to a IP4_RXDATA_WRAP, which will be\r
+  delivered to the upper layer. Each IP4 child that accepts the\r
+  packet will get a not-shared copy of the packet which is wrapped\r
+  in the IP4_RXDATA_WRAP. The IP4_RXDATA_WRAP->RxData is passed\r
+  to the upper layer. Upper layer will signal the recycle event in\r
+  it when it is done with the packet.\r
+\r
+  @param  IpInstance             The IP4 child to receive the packet\r
+  @param  Packet                 The packet to deliver up.\r
+\r
+  @return NULL if failed to wrap the packet, otherwise the wrapper.\r
+\r
+**/\r
+IP4_RXDATA_WRAP *\r
+Ip4WrapRxData (\r
+  IN IP4_PROTOCOL           *IpInstance,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP4_RXDATA_WRAP           *Wrap;\r
+  EFI_IP4_RECEIVE_DATA      *RxData;\r
+  EFI_STATUS                Status;\r
+\r
+  Wrap = NetAllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum));\r
+\r
+  if (Wrap == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NetListInit (&Wrap->Link);\r
+\r
+  Wrap->IpInstance  = IpInstance;\r
+  Wrap->Packet      = Packet;\r
+  RxData            = &Wrap->RxData;\r
+\r
+  NetZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  NET_TPL_RECYCLE,\r
+                  Ip4OnRecyclePacket,\r
+                  Wrap,\r
+                  &RxData->RecycleSignal\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (Wrap);\r
+    return NULL;\r
+  }\r
+\r
+  ASSERT (Packet->Ip != NULL);\r
+\r
+  //\r
+  // The application expects a network byte order header.\r
+  //\r
+  RxData->HeaderLength  = (Packet->Ip->HeadLen << 2);\r
+  RxData->Header        = (EFI_IP4_HEADER *) Ip4NtohHead (Packet->Ip);\r
+\r
+  RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN;\r
+  RxData->Options       = NULL;\r
+\r
+  if (RxData->OptionsLength != 0) {\r
+    RxData->Options = (VOID *) (RxData->Header + 1);\r
+  }\r
+\r
+  RxData->DataLength  = Packet->TotalSize;\r
+\r
+  //\r
+  // Build the fragment table to be delivered up.\r
+  //\r
+  RxData->FragmentCount = Packet->BlockOpNum;\r
+  NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);\r
+\r
+  return Wrap;\r
+}\r
+\r
+\r
+/**\r
+  Deliver the received packets to upper layer if there are both received\r
+  requests and enqueued packets. If the enqueued packet is shared, it will\r
+  duplicate it to a non-shared packet, release the shared packet, then\r
+  deliver the non-shared packet up.\r
+\r
+  @param  IpInstance             The IP child to deliver the packet up.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to deliver the\r
+                                 packets.\r
+  @retval EFI_SUCCESS            All the enqueued packets that can be delivered\r
+                                 are delivered up.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4InstanceDeliverPacket (\r
+  IN IP4_PROTOCOL           *IpInstance\r
+  )\r
+{\r
+  EFI_IP4_COMPLETION_TOKEN  *Token;\r
+  IP4_RXDATA_WRAP           *Wrap;\r
+  NET_BUF                   *Packet;\r
+  NET_BUF                   *Dup;\r
+  UINT8                     *Head;\r
+\r
+  //\r
+  // Deliver a packet if there are both a packet and a receive token.\r
+  //\r
+  while (!NetListIsEmpty (&IpInstance->Received) &&\r
+         !NetMapIsEmpty (&IpInstance->RxTokens)) {\r
+\r
+    Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);\r
+\r
+    if (!NET_BUF_SHARED (Packet)) {\r
+      //\r
+      // If this is the only instance that wants the packet, wrap it up.\r
+      //\r
+      Wrap = Ip4WrapRxData (IpInstance, Packet);\r
+\r
+      if (Wrap == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      NetListRemoveEntry (&Packet->List);\r
+\r
+    } else {\r
+      //\r
+      // Create a duplicated packet if this packet is shared\r
+      //\r
+      Dup = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);\r
+\r
+      if (Dup == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      //\r
+      // Copy the IP head over. The packet to deliver up is\r
+      // headless. Trim the head off after copy. The IP head\r
+      // may be not continuous before the data.\r
+      //\r
+      Head    = NetbufAllocSpace (Dup, IP4_MAX_HEADLEN, NET_BUF_HEAD);\r
+      Dup->Ip = (IP4_HEAD *) Head;\r
+\r
+      NetCopyMem (Head, Packet->Ip, Packet->Ip->HeadLen << 2);\r
+      NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE);\r
+\r
+      Wrap = Ip4WrapRxData (IpInstance, Dup);\r
+\r
+      if (Wrap == NULL) {\r
+        NetbufFree (Dup);\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      NetListRemoveEntry (&Packet->List);\r
+      NetbufFree (Packet);\r
+\r
+      Packet = Dup;\r
+    }\r
+\r
+    //\r
+    // Insert it into the delivered packet, then get a user's\r
+    // receive token, pass the wrapped packet up.\r
+    //\r
+    NET_TRYLOCK (&IpInstance->RecycleLock);\r
+    NetListInsertHead (&IpInstance->Delivered, &Wrap->Link);\r
+    NET_UNLOCK (&IpInstance->RecycleLock);\r
+\r
+    Token                = NetMapRemoveHead (&IpInstance->RxTokens, NULL);\r
+    Token->Status        = IP4_GET_CLIP_INFO (Packet)->Status;\r
+    Token->Packet.RxData = &Wrap->RxData;\r
+\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Enqueue a received packet to all the IP children that share\r
+  the same interface.\r
+\r
+  @param  IpSb                   The IP4 service instance that receive the packet\r
+  @param  Head                   The header of the received packet\r
+  @param  Packet                 The data of the received packet\r
+  @param  IpIf                   The interface to enqueue the packet to\r
+\r
+  @return The number of the IP4 children that accepts the packet\r
+\r
+**/\r
+INTN\r
+Ip4InterfaceEnquePacket (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet,\r
+  IN IP4_INTERFACE          *IpIf\r
+  )\r
+{\r
+  IP4_PROTOCOL              *IpInstance;\r
+  IP4_CLIP_INFO             *Info;\r
+  NET_LIST_ENTRY            *Entry;\r
+  INTN                      Enqueued;\r
+  INTN                      LocalType;\r
+  INTN                      SavedType;\r
+\r
+  //\r
+  // First, check that the packet is acceptable to this interface\r
+  // and find the local cast type for the interface. A packet sent\r
+  // to say 192.168.1.1 should NOT be delliever to 10.0.0.1 unless\r
+  // promiscuous receiving.\r
+  //\r
+  LocalType = 0;\r
+  Info      = IP4_GET_CLIP_INFO (Packet);\r
+\r
+  if ((Info->CastType == IP4_MULTICAST) || (Info->CastType == IP4_LOCAL_BROADCAST)) {\r
+    //\r
+    // If the CastType is multicast, don't need to filter against\r
+    // the group address here, Ip4InstanceFrameAcceptable will do\r
+    // that later.\r
+    //\r
+    LocalType = Info->CastType;\r
+\r
+  } else {\r
+    //\r
+    // Check the destination againist local IP. If the station\r
+    // address is 0.0.0.0, it means receiving all the IP destined\r
+    // to local non-zero IP. Otherwise, it is necessary to compare\r
+    // the destination to the interface's IP address.\r
+    //\r
+    if (IpIf->Ip == IP4_ALLZERO_ADDRESS) {\r
+      LocalType = IP4_LOCAL_HOST;\r
+\r
+    } else {\r
+      LocalType = Ip4GetNetCast (Head->Dst, IpIf);\r
+\r
+      if ((LocalType == 0) && IpIf->PromiscRecv) {\r
+        LocalType = IP4_PROMISCUOUS;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (LocalType == 0) {\r
+    return 0;\r
+  }\r
+\r
+  //\r
+  // Iterate through the ip instances on the interface, enqueue\r
+  // the packet if filter passed. Save the original cast type,\r
+  // and pass the local cast type to the IP children on the\r
+  // interface. The global cast type will be restored later.\r
+  //\r
+  SavedType       = Info->CastType;\r
+  Info->CastType  = LocalType;\r
+\r
+  Enqueued        = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
+    IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);\r
+    NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE);\r
+\r
+    if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {\r
+      Enqueued++;\r
+    }\r
+  }\r
+\r
+  Info->CastType = SavedType;\r
+  return Enqueued;\r
+}\r
+\r
+\r
+/**\r
+  Deliver the packet for each IP4 child on the interface.\r
+\r
+  @param  IpSb                   The IP4 service instance that received the packet\r
+  @param  IpIf                   The IP4 interface to deliver the packet.\r
+\r
+  @retval EFI_SUCCESS            It always returns EFI_SUCCESS now\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4InterfaceDeliverPacket (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_INTERFACE          *IpIf\r
+  )\r
+{\r
+  IP4_PROTOCOL              *Ip4Instance;\r
+  NET_LIST_ENTRY            *Entry;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
+    Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);\r
+    Ip4InstanceDeliverPacket (Ip4Instance);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Demultiple the packet. the packet delivery is processed in two\r
+  passes. The first pass will enque a shared copy of the packet\r
+  to each IP4 child that accepts the packet. The second pass will\r
+  deliver a non-shared copy of the packet to each IP4 child that\r
+  has pending receive requests. Data is copied if more than one\r
+  child wants to consume the packet bacause each IP child need\r
+  its own copy of the packet to make changes.\r
+\r
+  @param  IpSb                   The IP4 service instance that received the packet\r
+  @param  Head                   The header of the received packet\r
+  @param  Packet                 The data of the received packet\r
+\r
+  @retval EFI_NOT_FOUND          No IP child accepts the packet\r
+  @retval EFI_SUCCESS            The packet is enqueued or delivered to some IP\r
+                                 children.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4Demultiplex (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_HEAD               *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  IP4_INTERFACE             *IpIf;\r
+  INTN                      Enqueued;\r
+\r
+  //\r
+  // Two pass delivery: first, enque a shared copy of the packet\r
+  // to each instance that accept the packet.\r
+  //\r
+  Enqueued = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
+\r
+    if (IpIf->Configured) {\r
+      Enqueued += Ip4InterfaceEnquePacket (IpSb, Head, Packet, IpIf);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Second: deliver a duplicate of the packet to each instance.\r
+  // Release the local reference first, so that the last instance\r
+  // getting the packet will not copy the data.\r
+  //\r
+  NetbufFree (Packet);\r
+\r
+  if (Enqueued == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
+\r
+    if (IpIf->Configured) {\r
+      Ip4InterfaceDeliverPacket (IpSb, IpIf);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Timeout the fragment and enqueued packets.\r
+\r
+  @param  IpSb                   The IP4 service instance to timeout\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4PacketTimerTicking (\r
+  IN IP4_SERVICE            *IpSb\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *InstanceEntry;\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_PROTOCOL              *IpInstance;\r
+  IP4_ASSEMBLE_ENTRY        *Assemble;\r
+  NET_BUF                   *Packet;\r
+  IP4_CLIP_INFO             *Info;\r
+  UINT32                    Index;\r
+\r
+  //\r
+  // First, time out the fragments. The packet's life is counting down\r
+  // once the first-arrived fragment was received.\r
+  //\r
+  for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->Assemble.Bucket[Index]) {\r
+      Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);\r
+\r
+      if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {\r
+        NetListRemoveEntry (Entry);\r
+        Ip4FreeAssembleEntry (Assemble);\r
+      }\r
+    }\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {\r
+    IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP4_PROTOCOL, Link);\r
+\r
+    //\r
+    // Second, time out the assembled packets enqueued on each IP child.\r
+    //\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {\r
+      Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+      Info   = IP4_GET_CLIP_INFO (Packet);\r
+\r
+      if ((Info->Life > 0) && (--Info->Life == 0)) {\r
+        NetListRemoveEntry (Entry);\r
+        NetbufFree (Packet);\r
+      }\r
+    }\r
+\r
+    //\r
+    // Third: time out the transmitted packets.\r
+    //\r
+    NetMapIterate (&IpInstance->TxTokens, Ip4SentPacketTicking, NULL);\r
+  }\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h
new file mode 100644 (file)
index 0000000..9a3af8f
--- /dev/null
@@ -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 (file)
index 0000000..e1f059e
--- /dev/null
@@ -0,0 +1,231 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Ip4Option.c\r
+\r
+Abstract:\r
+\r
+  IP4 option support functions\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+\r
+/**\r
+  Validate the IP4 option format for both the packets we received\r
+  and will transmit. It will compute the ICMP error message fields\r
+  if the option is mal-formated. But this information isn't used.\r
+\r
+  @param  Option                The first byte of the option\r
+  @param  OptionLen             The length of the whole option\r
+  @param  Rcvd                  The option is from the packet we received if TRUE,\r
+                                otherwise the option we wants to transmit.\r
+\r
+  @return TRUE:  The option is properly formated\r
+  @return FALSE: The option is mal-formated\r
+\r
+**/\r
+BOOLEAN\r
+Ip4OptionIsValid (\r
+  IN UINT8                  *Option,\r
+  IN UINT32                 OptionLen,\r
+  IN BOOLEAN                Rcvd\r
+  )\r
+{\r
+  UINT32                    Cur;\r
+  UINT32                    Len;\r
+  UINT32                    Point;\r
+  UINT8                     IcmpType;\r
+  UINT8                     IcmpCode;\r
+  UINT32                    IcmpPoint;\r
+\r
+  IcmpType  = ICMP_PARAMETER_PROBLEM;\r
+  IcmpCode  = 0;\r
+  IcmpPoint = 0;\r
+\r
+  Cur       = 0;\r
+\r
+  while (Cur < OptionLen) {\r
+    switch (Option[Cur]) {\r
+    case IP4_OPTION_NOP:\r
+      Cur++;\r
+      break;\r
+\r
+    case IP4_OPTION_EOP:\r
+      Cur = OptionLen;\r
+      break;\r
+\r
+    case IP4_OPTION_LSRR:\r
+    case IP4_OPTION_SSRR:\r
+    case IP4_OPTION_RR:\r
+      Len   = Option[Cur + 1];\r
+      Point = Option[Cur + 2];\r
+\r
+      //\r
+      // SRR/RR options are formated as |Type|Len|Point|Ip1|Ip2|...\r
+      //\r
+      if ((OptionLen - Cur < Len) || (Len < 3) || ((Len - 3) % 4 != 0)) {\r
+        IcmpPoint = Cur + 1;\r
+        return FALSE;\r
+      }\r
+\r
+      if ((Point > Len + 1) || (Point % 4 != 0)) {\r
+        IcmpPoint = Cur + 2;\r
+        return FALSE;\r
+      }\r
+\r
+      //\r
+      // The Point must point pass the last entry if the packet is received\r
+      // by us. It must point to 4 if the packet is to be sent by us for\r
+      // source route option.\r
+      //\r
+      if ((Option[Cur] != IP4_OPTION_RR) &&\r
+          ((Rcvd && (Point != Len + 1)) || (!Rcvd && (Point != 4)))) {\r
+\r
+        IcmpType  = ICMP_DEST_UNREACHABLE;\r
+        IcmpCode  = ICMP_SOURCEROUTE_FAILED;\r
+        return FALSE;\r
+      }\r
+\r
+      Cur += Len;\r
+      break;\r
+\r
+    default:\r
+      Len = Option[Cur + 1];\r
+\r
+      if ((OptionLen - Cur < Len) || (Len < 2)) {\r
+        IcmpPoint = Cur + 1;\r
+        return FALSE;\r
+      }\r
+\r
+      Cur = Cur + Len;\r
+      break;\r
+    }\r
+\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Copy the option from the original option to buffer. It\r
+  handles the details such as:\r
+  1. whether copy the single IP4 option to the first/non-first\r
+  fragments.\r
+  2. Pad the options copied over to aligened to 4 bytes.\r
+\r
+  @param  Option                The original option to copy from\r
+  @param  OptionLen             The length of the original option\r
+  @param  FirstFragment         Whether it is the first fragment\r
+  @param  Buf                   The buffer to copy options to\r
+  @param  BufLen                The length of the buffer\r
+\r
+  @retval EFI_SUCCESS           The options are copied over\r
+  @retval EFI_BUFFER_TOO_SMALL  The buffer caller provided is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4CopyOption (\r
+  IN UINT8                  *Option,\r
+  IN UINT32                 OptionLen,\r
+  IN BOOLEAN                FirstFragment,\r
+  IN UINT8                  *Buf,           OPTIONAL\r
+  IN OUT UINT32             *BufLen\r
+  )\r
+{\r
+  UINT8                     OptBuf[40];\r
+  UINT32                    Cur;\r
+  UINT32                    Next;\r
+  UINT8                     Type;\r
+  UINT32                    Len;\r
+\r
+  ASSERT ((BufLen != NULL) && (OptionLen <= 40));\r
+\r
+  Cur   = 0;\r
+  Next  = 0;\r
+\r
+  while (Cur < OptionLen) {\r
+    Type  = Option[Cur];\r
+    Len   = Option[Cur + 1];\r
+\r
+    if (Type == IP4_OPTION_NOP) {\r
+      //\r
+      // Keep the padding, in case that the sender wants to align\r
+      // the option, say, to 4 bytes\r
+      //\r
+      OptBuf[Next] = IP4_OPTION_NOP;\r
+      Next++;\r
+      Cur++;\r
+\r
+    } else if (Type == IP4_OPTION_EOP) {\r
+      //\r
+      // Don't append the EOP to avoid including only a EOP option\r
+      //\r
+      break;\r
+\r
+    } else {\r
+      //\r
+      // don't copy options that is only valid for the first fragment\r
+      //\r
+      if (FirstFragment || (Type & IP4_OPTION_COPY_MASK)) {\r
+        NetCopyMem (OptBuf + Next, Option + Cur, Len);\r
+        Next += Len;\r
+      }\r
+\r
+      Cur += Len;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Don't append an EOP only option.\r
+  //\r
+  if (Next == 0) {\r
+    *BufLen = 0;\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Append an EOP if the end of option doesn't coincide with the\r
+  // end of the IP header, that is, isn't aligned to 4 bytes..\r
+  //\r
+  if ((Next % 4) != 0) {\r
+    OptBuf[Next] = IP4_OPTION_EOP;\r
+    Next++;\r
+  }\r
+\r
+  //\r
+  // Head length is in the unit of 4 bytes. Now, Len is the\r
+  // acutal option length to appear in the IP header.\r
+  //\r
+  Len = ((Next + 3) &~0x03);\r
+\r
+  //\r
+  // If the buffer is too small, set the BufLen then return\r
+  //\r
+  if ((Buf == NULL) || (*BufLen < Len)) {\r
+    *BufLen = Len;\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  //\r
+  // Copy the option to the Buf, zero the buffer first to pad\r
+  // the options with NOP to align to 4 bytes.\r
+  //\r
+  NetZeroMem (Buf, Len);\r
+  NetCopyMem (Buf, OptBuf, Next);\r
+  *BufLen = Len;\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h
new file mode 100644 (file)
index 0000000..6af885f
--- /dev/null
@@ -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 (file)
index 0000000..d595ab2
--- /dev/null
@@ -0,0 +1,457 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Ip4Output.c\r
+\r
+Abstract:\r
+\r
+  Transmit the IP4 packet\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+UINT16  mIp4Id;\r
+\r
+\r
+/**\r
+  Prepend an IP4 head to the Packet. It will copy the options and\r
+  build the IP4 header fields. Used for IP4 fragmentation.\r
+\r
+  @param  Packet               The packet to prepend IP4 header to\r
+  @param  Head                 The caller supplied header. The caller should set\r
+                               the  following header fields: Tos, TotalLen, Id,\r
+                               Fragment, Ttl, Protocol, Src and Dst. All the fields\r
+                               are in host byte order. This function will fill in\r
+                               the Ver, HeadLen, and checksum.\r
+  @param  Option               The orginal IP4 option to copy from\r
+  @param  OptLen               The length of the IP4 option\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE  There is no enought room in the head space of\r
+                               Packet.\r
+  @retval EFI_SUCCESS          The IP4 header is successfully added to the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4PrependHead (\r
+  IN NET_BUF                *Packet,\r
+  IN IP4_HEAD               *Head,\r
+  IN UINT8                  *Option,\r
+  IN UINT32                 OptLen\r
+  )\r
+{\r
+  UINT32                    HeadLen;\r
+  UINT32                    Len;\r
+  IP4_HEAD                  *PacketHead;\r
+  BOOLEAN                   FirstFragment;\r
+\r
+  //\r
+  // Prepend the options: first get the option length, then copy it over.\r
+  //\r
+  HeadLen       = 0;\r
+  FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment);\r
+\r
+  Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len);\r
+\r
+  HeadLen = IP4_MIN_HEADLEN + Len;\r
+  ASSERT (((Len %4) == 0) && (HeadLen <= IP4_MAX_HEADLEN));\r
+\r
+  PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);\r
+\r
+  if (PacketHead == NULL) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len);\r
+\r
+  //\r
+  // Set the head up, convert the host byte order to network byte order\r
+  //\r
+  PacketHead->Ver       = 4;\r
+  PacketHead->HeadLen   = (UINT8) (HeadLen >> 2);\r
+  PacketHead->Tos       = Head->Tos;\r
+  PacketHead->TotalLen  = HTONS (Packet->TotalSize);\r
+  PacketHead->Id        = HTONS (Head->Id);\r
+  PacketHead->Fragment  = HTONS (Head->Fragment);\r
+  PacketHead->Checksum  = 0;\r
+  PacketHead->Ttl       = Head->Ttl;\r
+  PacketHead->Protocol  = Head->Protocol;\r
+  PacketHead->Src       = HTONL (Head->Src);\r
+  PacketHead->Dst       = HTONL (Head->Dst);\r
+  PacketHead->Checksum  = ~NetblockChecksum ((UINT8 *) PacketHead, HeadLen);\r
+\r
+  Packet->Ip            = PacketHead;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Select an interface to send the packet generated in the IP4 driver\r
+  itself, that is, not by the requests of IP4 child's consumer. Such\r
+  packets include the ICMP echo replies, and other ICMP error packets.\r
+\r
+  @param  IpSb                 The IP4 service that wants to send the packets.\r
+  @param  Dst                  The destination of the packet\r
+  @param  Src                  The source of the packet\r
+\r
+  @return NULL if no proper interface is found, otherwise the interface that\r
+  @return can be used to send the system packet from.\r
+\r
+**/\r
+IP4_INTERFACE *\r
+Ip4SelectInterface (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_ADDR               Dst,\r
+  IN IP4_ADDR               Src\r
+  )\r
+{\r
+  IP4_INTERFACE             *IpIf;\r
+  IP4_INTERFACE             *Selected;\r
+  NET_LIST_ENTRY            *Entry;\r
+\r
+  //\r
+  // Select the interface the Dst is on if one of the connected\r
+  // network. Some IP instance may be configured with 0.0.0.0/0,\r
+  // don't select that interface now.\r
+  //\r
+  IpIf = Ip4FindNet (IpSb, Dst);\r
+\r
+  if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {\r
+    return IpIf;\r
+  }\r
+\r
+  //\r
+  // If source is one of the interface address, select it.\r
+  //\r
+  IpIf = Ip4FindInterface (IpSb, Src);\r
+\r
+  if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {\r
+    return IpIf;\r
+  }\r
+\r
+  //\r
+  // Select a configured interface as the fall back. Always prefer\r
+  // an interface with non-zero address.\r
+  //\r
+  Selected = NULL;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
+\r
+    if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) {\r
+      Selected = IpIf;\r
+    }\r
+  }\r
+\r
+  return Selected;\r
+}\r
+\r
+\r
+/**\r
+  The default callback function for system generated packet.\r
+  It will free the packet.\r
+\r
+  @param  Ip4Instance          The IP4 child that issued the transmission.  It most\r
+                               like is NULL.\r
+  @param  Packet               The packet that transmitted.\r
+  @param  IoStatus             The result of the transmission, succeeded or failed.\r
+  @param  LinkFlag             Not used when transmission. check IP4_FRAME_CALLBACK\r
+                               for reference.\r
+  @param  Context              The context provided by us\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4SysPacketSent (\r
+  IP4_PROTOCOL              *Ip4Instance,\r
+  NET_BUF                   *Packet,\r
+  EFI_STATUS                IoStatus,\r
+  UINT32                    LinkFlag,\r
+  VOID                      *Context\r
+  )\r
+{\r
+  NetbufFree (Packet);\r
+}\r
+\r
+\r
+/**\r
+  Transmit an IP4 packet. The packet comes either from the IP4\r
+  child's consumer (IpInstance != NULL) or the IP4 driver itself\r
+  (IpInstance == NULL). It will route the packet, fragment it,\r
+  then transmit all the fragments through some interface.\r
+\r
+  @param  IpSb                 The IP4 service instance to transmit the packet\r
+  @param  IpInstance           The IP4 child that issues the transmission.  It is\r
+                               NULL if the packet is from the system.\r
+  @param  Packet               The user data to send, excluding the IP header.\r
+  @param  Head                 The caller supplied header. The caller should set\r
+                               the  following header fields: Tos, TotalLen, Id, tl,\r
+                               Fragment, Protocol, Src and Dst. All the fields are\r
+                               in host byte  order. This function will fill in the\r
+                               Ver, HeadLen,  Fragment, and checksum. The Fragment\r
+                               only need to include the DF flag. Ip4Output will\r
+                               compute the MF and offset for  you.\r
+  @param  Option               The original option to append to the IP headers\r
+  @param  OptLen               The length of the option\r
+  @param  GateWay              The next hop address to transmit packet to.\r
+                               255.255.255.255 means broadcast.\r
+  @param  Callback             The callback function to issue when transmission\r
+                               completed.\r
+  @param  Context              The opaque context for the callback\r
+\r
+  @retval EFI_NO_MAPPING       There is no interface to the destination.\r
+  @retval EFI_NOT_FOUND        There is no route to the destination\r
+  @retval EFI_SUCCESS          The packet is successfully transmitted.\r
+  @retval Others               Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4Output (\r
+  IN IP4_SERVICE            *IpSb,\r
+  IN IP4_PROTOCOL           *IpInstance, OPTIONAL\r
+  IN NET_BUF                *Packet,\r
+  IN IP4_HEAD               *Head,\r
+  IN UINT8                  *Option,\r
+  IN UINT32                 OptLen,\r
+  IN IP4_ADDR               GateWay,\r
+  IN IP4_FRAME_CALLBACK     Callback,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP4_INTERFACE             *IpIf;\r
+  IP4_ROUTE_CACHE_ENTRY     *CacheEntry;\r
+  IP4_ADDR                  Dest;\r
+  EFI_STATUS                Status;\r
+  NET_BUF                   *Fragment;\r
+  UINT32                    Index;\r
+  UINT32                    HeadLen;\r
+  UINT32                    PacketLen;\r
+  UINT32                    Offset;\r
+  UINT32                    Mtu;\r
+  UINT32                    Num;\r
+\r
+  //\r
+  // Select an interface/source for system packet, application\r
+  // should select them itself.\r
+  //\r
+  if (IpInstance == NULL) {\r
+    IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src);\r
+  } else {\r
+    IpIf = IpInstance->Interface;\r
+  }\r
+\r
+  if (IpIf == NULL) {\r
+    return EFI_NO_MAPPING;\r
+  }\r
+\r
+  if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) {\r
+    Head->Src = IpIf->Ip;\r
+  }\r
+\r
+  //\r
+  // Route the packet unless overrided, that is, GateWay isn't zero.\r
+  //\r
+  if (GateWay == IP4_ALLZERO_ADDRESS) {\r
+    Dest = Head->Dst;\r
+\r
+    if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) {\r
+      //\r
+      // Set the gateway to local broadcast if the Dest is\r
+      // is the broadcast address for the connected network\r
+      // or it is local broadcast.\r
+      //\r
+      GateWay = IP4_ALLONE_ADDRESS;\r
+\r
+    } else if (IP4_IS_MULTICAST (Dest)) {\r
+      //\r
+      // Set the gateway to the destination if it is an multicast\r
+      // address. The IP4_INTERFACE won't consult ARP to send local\r
+      // broadcast and multicast.\r
+      //\r
+      GateWay = Head->Dst;\r
+\r
+    } else {\r
+      //\r
+      // Consult the route table to route the packet\r
+      //\r
+      if (IpInstance == NULL) {\r
+        CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);\r
+      } else {\r
+        CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src);\r
+      }\r
+\r
+      if (CacheEntry == NULL) {\r
+        return EFI_NOT_FOUND;\r
+      }\r
+\r
+      GateWay = CacheEntry->NextHop;\r
+      Ip4FreeRouteCacheEntry (CacheEntry);\r
+    }\r
+  }\r
+\r
+  //\r
+  // OK, selected the source and route, fragment the packet then send\r
+  // them. Tag each fragment other than the first one as spawn from it.\r
+  //\r
+  Mtu            = IpSb->SnpMode.MaxPacketSize;\r
+  HeadLen        = sizeof (IP4_HEAD) + (OptLen + 3) &~0x03;\r
+  Head->Id       = mIp4Id++;\r
+\r
+  if (Packet->TotalSize + HeadLen > Mtu) {\r
+    //\r
+    // Packet is fragmented from the tail to the head, that is, the\r
+    // first frame sent is the last fragment of the packet. The first\r
+    // fragment is NOT sent in this loop. First compute how many\r
+    // fragments there are.\r
+    //\r
+    Mtu       = (Mtu - HeadLen) & (~0x07);\r
+    Num       = (Packet->TotalSize + Mtu - 1) / Mtu;\r
+\r
+    //\r
+    // Initialize the packet length and Offset. Other than the last\r
+    // fragment, the packet length equals to MTU. The offset is always\r
+    // aligned to MTU.\r
+    //\r
+    PacketLen = Packet->TotalSize - (Num - 1) * Mtu;\r
+    Offset    = Mtu * (Num - 1);\r
+\r
+    for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) {\r
+      Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN);\r
+\r
+      if (Fragment == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      //\r
+      // Update the header's fragment. The caller fills the IP4 header\r
+      // fields that are required by Ip4PrependHead except the fragment.\r
+      //\r
+      Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset);\r
+      Ip4PrependHead (Fragment, Head, Option, OptLen);\r
+\r
+      //\r
+      // Transmit the fragments, pass the Packet address as the context.\r
+      // So, we can find all the fragments spawned from the Packet by\r
+      // compare the NetBuf and Context to the Packet.\r
+      //\r
+      Status = Ip4SendFrame (\r
+                 IpIf,\r
+                 IpInstance,\r
+                 Fragment,\r
+                 GateWay,\r
+                 Ip4SysPacketSent,\r
+                 Packet\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      PacketLen = Mtu;\r
+    }\r
+\r
+    //\r
+    // Trim the already sent data, then adjust the head's fragment field.\r
+    //\r
+    NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE);\r
+    Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0);\r
+  }\r
+\r
+  //\r
+  // Send the first fragment, it is either the orginal packet, or the\r
+  // first fragment of a fragmented packet. It seems that there is a subtle\r
+  // bug here: what if the caller free the packet in Callback and IpIf (or\r
+  // MNP child used by that interface) still holds the fragments and try\r
+  // to access the data? The caller can free the packet if it recycles the\r
+  // consumer's (such as UDP) data in the Callback. But this can't happen.\r
+  // The detailed sequence is:\r
+  // 1. for the packets generated by IP4 driver itself:\r
+  //    The Callback is Ip4SysPacketSent, which is the same as the\r
+  //    fragments' callback. Ip4SysPacketSent simply calls NetbufFree\r
+  //    to release its reference to the packet. So, no problem for\r
+  //    system packets.\r
+  //\r
+  // 2. for the upper layer's packets (use UDP as an example):\r
+  //    UDP requests the IP layer to transmit some data which is\r
+  //    wrapped in an asynchronous token, the token is wrapped\r
+  //    in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data\r
+  //    in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP\r
+  //    is bound with the Packet. It will only be freed when all\r
+  //    the references to Packet have been released. Upon then, the\r
+  //    Packet's OnFree callback will release the IP4_TXTOKEN_WRAP,\r
+  //    and singal the user's recycle event. So, also no problem for\r
+  //    upper layer's packets.\r
+  //\r
+  Ip4PrependHead (Packet, Head, Option, OptLen);\r
+  Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  Ip4CancelPacket (IpIf, Packet, Status);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The filter function to find a packet and all its fragments.\r
+  The packet's fragments have their Context set to the packet.\r
+\r
+  @param  Frame                The frames hold by the low level interface\r
+  @param  Context              Context to the function, which is the packet.\r
+\r
+  @retval TRUE                 This is the packet to cancel or its fragments.\r
+  @retval FALSE                This is unrelated packet.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+Ip4CancelPacketFragments (\r
+  IP4_LINK_TX_TOKEN   *Frame,\r
+  VOID                *Context\r
+  )\r
+{\r
+  if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  Cancel the Packet and all its fragments.\r
+\r
+  @param  IpIf                 The interface from which the Packet is sent\r
+  @param  Packet               The Packet to cancel\r
+  @param  IoStatus             The status returns to the sender.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4CancelPacket (\r
+  IN IP4_INTERFACE    *IpIf,\r
+  IN NET_BUF          *Packet,\r
+  IN EFI_STATUS       IoStatus\r
+  )\r
+{\r
+  Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h
new file mode 100644 (file)
index 0000000..e165e1d
--- /dev/null
@@ -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 (file)
index 0000000..2cf1d0f
--- /dev/null
@@ -0,0 +1,690 @@
+/** @file\r
+\r
+Copyright (c) 2005 - 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+Module Name:\r
+\r
+  Ip4Route.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Ip4Impl.h"\r
+\r
+\r
+/**\r
+  Allocate a route entry then initialize it with the Dest/Netmaks\r
+  and Gateway.\r
+\r
+  @param  Dest                  The destination network\r
+  @param  Netmask               The destination network mask\r
+  @param  GateWay               The nexthop address\r
+\r
+  @return NULL if failed to allocate memeory, otherwise the newly created\r
+  @return route entry.\r
+\r
+**/\r
+STATIC\r
+IP4_ROUTE_ENTRY *\r
+Ip4CreateRouteEntry (\r
+  IN IP4_ADDR               Dest,\r
+  IN IP4_ADDR               Netmask,\r
+  IN IP4_ADDR               GateWay\r
+  )\r
+{\r
+  IP4_ROUTE_ENTRY           *RtEntry;\r
+\r
+  RtEntry = NetAllocatePool (sizeof (IP4_ROUTE_ENTRY));\r
+\r
+  if (RtEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NetListInit (&RtEntry->Link);\r
+\r
+  RtEntry->RefCnt  = 1;\r
+  RtEntry->Dest    = Dest;\r
+  RtEntry->Netmask = Netmask;\r
+  RtEntry->NextHop = GateWay;\r
+  RtEntry->Flag    = 0;\r
+\r
+  return RtEntry;\r
+}\r
+\r
+\r
+/**\r
+  Free the route table entry. It is reference counted.\r
+\r
+  @param  RtEntry               The route entry to free.\r
+\r
+  @return NONE\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4FreeRouteEntry (\r
+  IN IP4_ROUTE_ENTRY    *RtEntry\r
+  )\r
+{\r
+  ASSERT (RtEntry->RefCnt > 0);\r
+\r
+  if (--RtEntry->RefCnt == 0) {\r
+    NetFreePool (RtEntry);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Allocate and initialize a IP4 route cache entry.\r
+\r
+  @param  Dst                   The destination address\r
+  @param  Src                   The source address\r
+  @param  GateWay               The next hop address\r
+  @param  Tag                   The tag from the caller. This marks all the cache\r
+                                entries spawned from one route table entry.\r
+\r
+  @return NULL if failed to allocate memory for the cache, other point\r
+  @return to the created route cache entry.\r
+\r
+**/\r
+STATIC\r
+IP4_ROUTE_CACHE_ENTRY *\r
+Ip4CreateRouteCacheEntry (\r
+  IN IP4_ADDR               Dst,\r
+  IN IP4_ADDR               Src,\r
+  IN IP4_ADDR               GateWay,\r
+  IN UINTN                  Tag\r
+  )\r
+{\r
+  IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+\r
+  RtCacheEntry = NetAllocatePool (sizeof (IP4_ROUTE_CACHE_ENTRY));\r
+\r
+  if (RtCacheEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NetListInit (&RtCacheEntry->Link);\r
+\r
+  RtCacheEntry->RefCnt  = 1;\r
+  RtCacheEntry->Dest    = Dst;\r
+  RtCacheEntry->Src     = Src;\r
+  RtCacheEntry->NextHop = GateWay;\r
+  RtCacheEntry->Tag     = Tag;\r
+\r
+  return RtCacheEntry;\r
+}\r
+\r
+\r
+/**\r
+  Free the route cache entry. It is reference counted.\r
+\r
+  @param  RtCacheEntry          The route cache entry to free.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4FreeRouteCacheEntry (\r
+  IN IP4_ROUTE_CACHE_ENTRY  *RtCacheEntry\r
+  )\r
+{\r
+  ASSERT (RtCacheEntry->RefCnt > 0);\r
+\r
+  if (--RtCacheEntry->RefCnt == 0) {\r
+    NetFreePool (RtCacheEntry);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Initialize an empty route cache table.\r
+\r
+  @param  RtCache               The rotue cache table to initialize.\r
+\r
+  @return NONE\r
+\r
+**/\r
+VOID\r
+Ip4InitRouteCache (\r
+  IN IP4_ROUTE_CACHE        *RtCache\r
+  )\r
+{\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {\r
+    NetListInit (&(RtCache->CacheBucket[Index]));\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Clean up a route cache, that is free all the route cache\r
+  entries enqueued in the cache.\r
+\r
+  @param  RtCache               The route cache table to clean up\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4CleanRouteCache (\r
+  IN IP4_ROUTE_CACHE        *RtCache\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtCache->CacheBucket[Index])) {\r
+      RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);\r
+\r
+      NetListRemoveEntry (Entry);\r
+      Ip4FreeRouteCacheEntry (RtCacheEntry);\r
+    }\r
+  }\r
+}\r
+\r
+\r
+\r
+/**\r
+  Create an empty route table, includes its internal route cache\r
+\r
+  None\r
+\r
+  @return NULL if failed to allocate memory for the route table, otherwise\r
+  @return the point to newly created route table.\r
+\r
+**/\r
+IP4_ROUTE_TABLE *\r
+Ip4CreateRouteTable (\r
+  VOID\r
+  )\r
+{\r
+  IP4_ROUTE_TABLE           *RtTable;\r
+  UINT32                    Index;\r
+\r
+  RtTable = NetAllocatePool (sizeof (IP4_ROUTE_TABLE));\r
+\r
+  if (RtTable == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  RtTable->RefCnt   = 1;\r
+  RtTable->TotalNum = 0;\r
+\r
+  for (Index = 0; Index < IP4_MASK_NUM; Index++) {\r
+    NetListInit (&(RtTable->RouteArea[Index]));\r
+  }\r
+\r
+  RtTable->Next = NULL;\r
+\r
+  Ip4InitRouteCache (&RtTable->Cache);\r
+  return RtTable;\r
+}\r
+\r
+\r
+/**\r
+  Free the route table and its associated route cache. Route\r
+  table is reference counted.\r
+\r
+  @param  RtTable               The route table to free.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Ip4FreeRouteTable (\r
+  IN IP4_ROUTE_TABLE        *RtTable\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_ROUTE_ENTRY           *RtEntry;\r
+  UINT32                    Index;\r
+\r
+  ASSERT (RtTable->RefCnt > 0);\r
+\r
+  if (--RtTable->RefCnt > 0) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Free all the route table entry and its route cache.\r
+  //\r
+  for (Index = 0; Index < IP4_MASK_NUM; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtTable->RouteArea[Index])) {\r
+      RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
+\r
+      NetListRemoveEntry (Entry);\r
+      Ip4FreeRouteEntry (RtEntry);\r
+    }\r
+  }\r
+\r
+  Ip4CleanRouteCache (&RtTable->Cache);\r
+\r
+  NetFreePool (RtTable);\r
+}\r
+\r
+\r
+\r
+/**\r
+  Remove all the cache entries bearing the Tag. When a route cache\r
+  entry is created, it is tagged with the address of route entry\r
+  from which it is spawned. When a route entry is deleted, the cache\r
+  entries spawned from it are also deleted.\r
+\r
+  @param  RtCache               Route cache to remove the entries from\r
+  @param  Tag                   The Tag of the entries to remove\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Ip4PurgeRouteCache (\r
+  IN IP4_ROUTE_CACHE        *RtCache,\r
+  IN UINTN                  Tag\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {\r
+\r
+      RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);\r
+\r
+      if (RtCacheEntry->Tag == Tag) {\r
+        NetListRemoveEntry (Entry);\r
+        Ip4FreeRouteCacheEntry (RtCacheEntry);\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Add a route entry to the route table. All the IP4_ADDRs are in\r
+  host byte order.\r
+\r
+  @param  RtTable               Route table to add route to\r
+  @param  Dest                  The destination of the network\r
+  @param  Netmask               The netmask of the destination\r
+  @param  Gateway               The next hop address\r
+\r
+  @retval EFI_ACCESS_DENIED     The same route already exists\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the entry\r
+  @retval EFI_SUCCESS           The route is added successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4AddRoute (\r
+  IN IP4_ROUTE_TABLE        *RtTable,\r
+  IN IP4_ADDR               Dest,\r
+  IN IP4_ADDR               Netmask,\r
+  IN IP4_ADDR               Gateway\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Head;\r
+  NET_LIST_ENTRY            *Entry;\r
+  IP4_ROUTE_ENTRY           *RtEntry;\r
+\r
+  //\r
+  // All the route entries with the same netmask length are\r
+  // linke to the same route area\r
+  //\r
+  Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]);\r
+\r
+  //\r
+  // First check whether the route exists\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, Head) {\r
+    RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
+\r
+    if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) {\r
+      return EFI_ACCESS_DENIED;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Create a route entry and insert it to the route area.\r
+  //\r
+  RtEntry = Ip4CreateRouteEntry (Dest, Netmask, Gateway);\r
+\r
+  if (RtEntry == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  if (Gateway == IP4_ALLZERO_ADDRESS) {\r
+    RtEntry->Flag = IP4_DIRECT_ROUTE;\r
+  }\r
+\r
+  NetListInsertHead (Head, &RtEntry->Link);\r
+  RtTable->TotalNum++;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Remove a route entry and all the route caches spawn from it.\r
+\r
+  @param  RtTable               The route table to remove the route from\r
+  @param  Dest                  The destination network\r
+  @param  Netmask               The netmask of the Dest\r
+  @param  Gateway               The next hop address\r
+\r
+  @retval EFI_SUCCESS           The route entry is successfully removed\r
+  @retval EFI_NOT_FOUND         There is no route entry in the table with that\r
+                                properity.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4DelRoute (\r
+  IN IP4_ROUTE_TABLE      *RtTable,\r
+  IN IP4_ADDR             Dest,\r
+  IN IP4_ADDR             Netmask,\r
+  IN IP4_ADDR             Gateway\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Head;\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_ROUTE_ENTRY           *RtEntry;\r
+\r
+  Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]);\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {\r
+    RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
+\r
+    if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) {\r
+      Ip4PurgeRouteCache (&RtTable->Cache, (UINTN) RtEntry);\r
+      NetListRemoveEntry (Entry);\r
+      Ip4FreeRouteEntry  (RtEntry);\r
+\r
+      RtTable->TotalNum--;\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+  Find a route cache with the dst and src. This is used by ICMP\r
+  redirect messasge process. All kinds of redirect is treated as\r
+  host redirect according to RFC1122. So, only route cache entries\r
+  are modified according to the ICMP redirect message.\r
+\r
+  @param  RtTable               The route table to search the cache for\r
+  @param  Dest                  The destination address\r
+  @param  Src                   The source address\r
+\r
+  @return NULL if no route entry to the (Dest, Src). Otherwise the point\r
+  @return to the correct route cache entry.\r
+\r
+**/\r
+IP4_ROUTE_CACHE_ENTRY *\r
+Ip4FindRouteCache (\r
+  IN IP4_ROUTE_TABLE        *RtTable,\r
+  IN IP4_ADDR               Dest,\r
+  IN IP4_ADDR               Src\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+  UINT32                    Index;\r
+\r
+  Index = IP4_ROUTE_CACHE_HASH (Dest, Src);\r
+\r
+  NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {\r
+    RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);\r
+\r
+    if ((RtCacheEntry->Dest == Dest) && (RtCacheEntry->Src == Src)) {\r
+      NET_GET_REF (RtCacheEntry);\r
+      return RtCacheEntry;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Search the route table for a most specific match to the Dst. It searches\r
+  from the longest route area (mask length == 32) to the shortest route area (\r
+  default routes). In each route area, it will first search the instance's\r
+  route table, then the default route table. This is required by the following\r
+  requirements:\r
+  1. IP search the route table for a most specific match\r
+  2. The local route entries have precedence over the default route entry.\r
+\r
+  @param  RtTable               The route table to search from\r
+  @param  Dst                   The destionation address to search\r
+\r
+  @return NULL if no route matches the Dst, otherwise the point to the\r
+  @return most specific route to the Dst.\r
+\r
+**/\r
+STATIC\r
+IP4_ROUTE_ENTRY *\r
+Ip4FindRouteEntry (\r
+  IN IP4_ROUTE_TABLE        *RtTable,\r
+  IN IP4_ADDR               Dst\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  IP4_ROUTE_ENTRY           *RtEntry;\r
+  IP4_ROUTE_TABLE           *Table;\r
+  INTN                      Index;\r
+\r
+  RtEntry = NULL;\r
+\r
+  for (Index = IP4_MASK_NUM - 1; Index >= 0; Index--) {\r
+    for (Table = RtTable; Table != NULL; Table = Table->Next) {\r
+      NET_LIST_FOR_EACH (Entry, &Table->RouteArea[Index]) {\r
+        RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
+\r
+        if (IP4_NET_EQUAL (RtEntry->Dest, Dst, RtEntry->Netmask)) {\r
+          NET_GET_REF (RtEntry);\r
+          return RtEntry;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Search the route table to route the packet. Return/creat a route\r
+  cache if there is a route to the destination.\r
+\r
+  @param  RtTable               The route table to search from\r
+  @param  Dest                  The destination address to search for\r
+  @param  Src                   The source address to search for\r
+\r
+  @return NULL if failed to route packet, otherwise a route cache\r
+  @return entry that can be used to route packet.\r
+\r
+**/\r
+IP4_ROUTE_CACHE_ENTRY *\r
+Ip4Route (\r
+  IN IP4_ROUTE_TABLE        *RtTable,\r
+  IN IP4_ADDR               Dest,\r
+  IN IP4_ADDR               Src\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Head;\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+  IP4_ROUTE_CACHE_ENTRY     *Cache;\r
+  IP4_ROUTE_ENTRY           *RtEntry;\r
+  IP4_ADDR                  NextHop;\r
+  UINT32                    Count;\r
+\r
+  ASSERT (RtTable != NULL);\r
+\r
+  Head          = &RtTable->Cache.CacheBucket[IP4_ROUTE_CACHE_HASH (Dest, Src)];\r
+  RtCacheEntry  = Ip4FindRouteCache (RtTable, Dest, Src);\r
+\r
+  //\r
+  // If found, promote the cache entry to the head of the hash bucket. LRU\r
+  //\r
+  if (RtCacheEntry != NULL) {\r
+    NetListRemoveEntry (&RtCacheEntry->Link);\r
+    NetListInsertHead (Head, &RtCacheEntry->Link);\r
+    return RtCacheEntry;\r
+  }\r
+\r
+  //\r
+  // Search the route table for the most specific route\r
+  //\r
+  RtEntry = Ip4FindRouteEntry (RtTable, Dest);\r
+\r
+  if (RtEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Found a route to the Dest, if it is a direct route, the packet\r
+  // will be send directly to the destination, such as for connected\r
+  // network. Otherwise, it is an indirect route, the packet will be\r
+  // send the next hop router.\r
+  //\r
+  if (RtEntry->Flag & IP4_DIRECT_ROUTE) {\r
+    NextHop = Dest;\r
+  } else {\r
+    NextHop = RtEntry->NextHop;\r
+  }\r
+\r
+  Ip4FreeRouteEntry (RtEntry);\r
+\r
+  //\r
+  // Create a route cache entry, and tag it as spawned from this route entry\r
+  //\r
+  RtCacheEntry = Ip4CreateRouteCacheEntry (Dest, Src, NextHop, (UINTN) RtEntry);\r
+\r
+  if (RtCacheEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NetListInsertHead (Head, &RtCacheEntry->Link);\r
+  NET_GET_REF (RtCacheEntry);\r
+\r
+  //\r
+  // Each bucket of route cache can contain at most 64 entries.\r
+  // Remove the entries at the tail of the bucket. These entries\r
+  // are likely to be used least.\r
+  //\r
+  Count = 0;\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {\r
+    if (++Count < IP4_ROUTE_CACHE_MAX) {\r
+      continue;\r
+    }\r
+\r
+    Cache = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);\r
+\r
+    NetListRemoveEntry (Entry);\r
+    Ip4FreeRouteCacheEntry (Cache);\r
+  }\r
+\r
+  return RtCacheEntry;\r
+}\r
+\r
+\r
+/**\r
+  Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of\r
+  GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the\r
+  internal operation of the IP4 driver.\r
+\r
+  @param  IpInstance            The IP4 child that requests the route table.\r
+\r
+  @retval EFI_SUCCESS           The route table is successfully build\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the rotue table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip4BuildEfiRouteTable (\r
+  IN IP4_PROTOCOL           *IpInstance\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+  NET_LIST_ENTRY            *Entry;\r
+  IP4_ROUTE_TABLE           *RtTable;\r
+  IP4_ROUTE_ENTRY           *RtEntry;\r
+  EFI_IP4_ROUTE_TABLE       *Table;\r
+  UINT32                    Count;\r
+  INT32                     Index;\r
+\r
+  IpSb    = IpInstance->Service;\r
+  RtTable = IpInstance->RouteTable;\r
+\r
+  if (IpInstance->EfiRouteTable != NULL) {\r
+    NetFreePool (IpInstance->EfiRouteTable);\r
+\r
+    IpInstance->EfiRouteTable = NULL;\r
+    IpInstance->EfiRouteCount = 0;\r
+  }\r
+\r
+  Count = RtTable->TotalNum;\r
+\r
+  if (RtTable->Next != NULL) {\r
+    Count += RtTable->Next->TotalNum;\r
+  }\r
+\r
+  if (Count == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Table = NetAllocatePool (sizeof (EFI_IP4_ROUTE_TABLE) * Count);\r
+\r
+  if (Table == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Copy the route entry to EFI route table. Keep the order of\r
+  // route entry copied from most specific to default route. That\r
+  // is, interlevel the route entry from the instance's route area\r
+  // and those from the default route table's route area.\r
+  //\r
+  Count = 0;\r
+\r
+  for (Index = IP4_MASK_NUM - 1; Index >= 0; Index--) {\r
+    for (RtTable = IpInstance->RouteTable; RtTable != NULL; RtTable = RtTable->Next) {\r
+      NET_LIST_FOR_EACH (Entry, &(RtTable->RouteArea[Index])) {\r
+        RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
+\r
+        EFI_IP4 (Table[Count].SubnetAddress)  = HTONL (RtEntry->Dest & RtEntry->Netmask);\r
+        EFI_IP4 (Table[Count].SubnetMask)     = HTONL (RtEntry->Netmask);\r
+        EFI_IP4 (Table[Count].GatewayAddress) = HTONL (RtEntry->NextHop);\r
+\r
+        Count++;\r
+      }\r
+    }\r
+  }\r
+\r
+  IpInstance->EfiRouteTable = Table;\r
+  IpInstance->EfiRouteCount = Count;\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h
new file mode 100644 (file)
index 0000000..ce07b1d
--- /dev/null
@@ -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
index 5a648f5ffc8e9a7b348e32d7cc7a93b280b41bb9..83fc3c6ae48e9a5ec202ee2e8b3b1c7f580854e0 100644 (file)
@@ -139,7 +139,7 @@ MnpAddFreeNbuf (
 \r
   for (Index = 0; Index < Count; Index++) {\r
 \r
 \r
   for (Index = 0; Index < Count; Index++) {\r
 \r
-    Nbuf = NetbufAlloc (MnpServiceData->BufferLength);\r
+    Nbuf = NetbufAlloc (MnpServiceData->BufferLength + MnpServiceData->PaddingSize);\r
     if (Nbuf == NULL) {\r
 \r
       MNP_DEBUG_ERROR (("MnpAddFreeNbuf: NetBufAlloc failed.\n"));\r
     if (Nbuf == NULL) {\r
 \r
       MNP_DEBUG_ERROR (("MnpAddFreeNbuf: NetBufAlloc failed.\n"));\r
@@ -147,6 +147,14 @@ MnpAddFreeNbuf (
       break;\r
     }\r
 \r
       break;\r
     }\r
 \r
+    if (MnpServiceData->PaddingSize > 0) {\r
+      //\r
+      // Pad padding bytes before the media header\r
+      //\r
+      NetbufAllocSpace (Nbuf, MnpServiceData->PaddingSize, NET_BUF_TAIL);\r
+      NetbufTrim (Nbuf, MnpServiceData->PaddingSize, NET_BUF_HEAD);\r
+    }\r
+\r
     NetbufQueAppend (&MnpServiceData->FreeNbufQue, Nbuf);\r
   }\r
 \r
     NetbufQueAppend (&MnpServiceData->FreeNbufQue, Nbuf);\r
   }\r
 \r
@@ -328,6 +336,12 @@ MnpInitializeServiceData (
   //\r
   MnpServiceData->BufferLength = MnpServiceData->Mtu + SnpMode->MediaHeaderSize + NET_ETHER_FCS_SIZE;\r
 \r
   //\r
   MnpServiceData->BufferLength = MnpServiceData->Mtu + SnpMode->MediaHeaderSize + NET_ETHER_FCS_SIZE;\r
 \r
+  //\r
+  // Make sure the protocol headers immediately following the media header \r
+  // 4-byte aligned\r
+  //\r
+  MnpServiceData->PaddingSize = (4 - SnpMode->MediaHeaderSize) & 0x3;\r
+\r
   //\r
   // Initialize the FreeNetBufQue and pre-allocate some NET_BUFs.\r
   //\r
   //\r
   // Initialize the FreeNetBufQue and pre-allocate some NET_BUFs.\r
   //\r
index 07be21dcedbcf8b84bdcc8c28433d6de2beb338c..932b21c5ef1d34de49213353b86f55cb0887f040 100644 (file)
@@ -521,7 +521,6 @@ MnpServiceBindingDestroyChild (
   return Status;\r
 }\r
 \r
   return Status;\r
 }\r
 \r
-//@MT: EFI_DRIVER_ENTRY_POINT (MnpDriverEntryPoint)\r
 \r
 EFI_STATUS\r
 EFIAPI\r
 \r
 EFI_STATUS\r
 EFIAPI\r
index 48ad0960cd45fa28848dfc2fd719ad8cd010cbde..a387eb05d003dd496257518a88a9065f9469564c 100644 (file)
@@ -82,6 +82,7 @@ typedef struct _MNP_SERVICE_DATA {
   // store a packet.
   //
   UINT32                        BufferLength;
   // store a packet.
   //
   UINT32                        BufferLength;
+  UINT32                        PaddingSize;
   NET_BUF                       *RxNbufCache;
   UINT8                         *TxBuf;
 } MNP_SERVICE_DATA;
   NET_BUF                       *RxNbufCache;
   UINT8                         *TxBuf;
 } MNP_SERVICE_DATA;
index 91b8d4e0b854e327c8b7413533d66b659052fb8b..aafc8433a52f8527120b04513aeef147c69eb0b8 100644 (file)
@@ -974,7 +974,10 @@ MnpReceivePacket (
     //\r
     // No receiver for this packet.\r
     //\r
     //\r
     // No receiver for this packet.\r
     //\r
-    NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL);\r
+    if (Trimmed > 0) {\r
+      NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL);\r
+    }\r
+\r
     goto EXIT;\r
   }\r
   //\r
     goto EXIT;\r
   }\r
   //\r
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c
new file mode 100644 (file)
index 0000000..2582994
--- /dev/null
@@ -0,0 +1,161 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  ComponentName.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Mtftp4Driver.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp4ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp4ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+EFI_COMPONENT_NAME_PROTOCOL     gMtftp4ComponentName = {\r
+  Mtftp4ComponentNameGetDriverName,\r
+  Mtftp4ComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+static EFI_UNICODE_STRING_TABLE mMtftp4DriverNameTable[] = {\r
+  {\r
+    "eng",\r
+    L"MTFTP4 Network Service"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp4ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the EFI Driver.\r
+\r
+  Arguments:\r
+    This           : A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    Language    : A pointer to a three character ISO 639-2 language identifier.\r
+                 This is the language of the driver name that that the caller\r
+                 is requesting, and it must match one of the languages specified\r
+                 in SupportedLanguages.  The number of languages supported by a\r
+                 driver is up to the driver writer.\r
+    DriverName  : A pointer to the Unicode string to return.  This Unicode string\r
+                 is the name of the driver specified by This in the language\r
+                 specified by Language.\r
+\r
+  Returns:\r
+    EFI_SUCCES      : The Unicode string for the Driver specified by This\r
+                            and the language specified by Language was returned\r
+                            in DriverName.\r
+    EFI_INVALID_PARAMETER : Language is NULL.\r
+    EFI_INVALID_PARAMETER : DriverName is NULL.\r
+    EFI_UNSUPPORTED       : The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return LookupUnicodeString (\r
+          Language,\r
+          gMtftp4ComponentName.SupportedLanguages,\r
+          mMtftp4DriverNameTable,\r
+          DriverName\r
+          );\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp4ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the controller\r
+    that is being managed by an EFI Driver.\r
+\r
+  Arguments:\r
+    This                     : A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    ControllerHandle     :The handle of a controller that the driver specified by\r
+                                This is managing.  This handle specifies the controller\r
+                                whose name is to be returned.\r
+    ChildHandle           :The handle of the child controller to retrieve the name\r
+                                of.  This is an optional parameter that may be NULL.  It\r
+                                will be NULL for device drivers.  It will also be NULL\r
+                               for a bus drivers that wish to retrieve the name of the\r
+                               bus controller.  It will not be NULL for a bus driver\r
+                               that wishes to retrieve the name of a child controller.\r
+    Language            : A pointer to a three character ISO 639-2 language\r
+                               identifier.  This is the language of the controller name\r
+                               that that the caller is requesting, and it must match one\r
+                               of the languages specified in SupportedLanguages.  The\r
+                               number of languages supported by a driver is up to the\r
+                                driver writer.\r
+    ControllerName    : A pointer to the Unicode string to return.  This Unicode\r
+                               string is the name of the controller specified by\r
+                               ControllerHandle and ChildHandle in the language specified\r
+                               by Language from the point of view of the driver specified\r
+                               by This.\r
+\r
+  Returns:\r
+    EFI_SUCCESS      :The Unicode string for the user readable name in the\r
+                            language specified by Language for the driver\r
+                            specified by This was returned in DriverName.\r
+    EFI_INVALID_PARAMETER : ControllerHandle is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER : ChildHandle is not NULL and it is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER : Language is NULL.\r
+    EFI_INVALID_PARAMETER : ControllerName is NULL.\r
+    EFI_UNSUPPORTED          : The driver specified by This is not currently managing\r
+                            the controller specified by ControllerHandle and\r
+                            ChildHandle.\r
+    EFI_UNSUPPORTED          :The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c
new file mode 100644 (file)
index 0000000..9b30b3d
--- /dev/null
@@ -0,0 +1,643 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Mtftp4Driver.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Mtftp4Impl.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL   gMtftp4DriverBinding = {\r
+  Mtftp4DriverBindingSupported,\r
+  Mtftp4DriverBindingStart,\r
+  Mtftp4DriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL  gMtftp4ServiceBindingTemplete = {\r
+  Mtftp4ServiceBindingCreateChild,\r
+  Mtftp4ServiceBindingDestroyChild\r
+};\r
+\r
+//@MT: EFI_DRIVER_ENTRY_POINT (Mtftp4DriverEntryPoint)\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp4DriverEntryPoint (\r
+  IN EFI_HANDLE             ImageHandle,\r
+  IN EFI_SYSTEM_TABLE       *SystemTable\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  The driver entry point which installs multiple protocols to the ImageHandle.\r
+\r
+Arguments:\r
+\r
+  ImageHandle - The MTFTP's image handle\r
+  SystemTable - The system table\r
+\r
+Returns:\r
+\r
+  EFI_SUCCESS - The handles are successfully installed on the image. Otherwise\r
+  some EFI_ERROR.\r
+\r
+--*/\r
+{\r
+  return NetLibInstallAllDriverProtocols (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gMtftp4DriverBinding,\r
+           ImageHandle,\r
+           &gMtftp4ComponentName,\r
+           NULL,\r
+           NULL\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Test whether MTFTP driver support this controller.\r
+\r
+  @param  This                   The MTFTP driver binding instance\r
+  @param  Controller             The controller to test\r
+  @param  RemainingDevicePath    The remaining device path\r
+\r
+  @retval EFI_SUCCESS            The controller has UDP service binding protocol\r
+                                 installed, MTFTP can support it.\r
+  @retval Others                 MTFTP can't support the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiUdp4ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Config a NULL UDP that is used to keep the connection between UDP\r
+  and MTFTP. Just leave the Udp child unconfigured. When UDP is\r
+  unloaded, MTFTP will be informed with DriverBinding Stop.\r
+\r
+  @param  UdpIo                  The UDP port to configure\r
+  @param  Context                The opaque parameter to the callback\r
+\r
+  @retval EFI_SUCCESS            It always return EFI_SUCCESS directly.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4ConfigNullUdp (\r
+  IN UDP_IO_PORT            *UdpIo,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Create then initialize a MTFTP service binding instance.\r
+\r
+  @param  Controller             The controller to install the MTFTP service\r
+                                 binding on\r
+  @param  Image                  The driver binding image of the MTFTP driver\r
+  @param  Service                The variable to receive the created service\r
+                                 binding instance.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource to create the instance\r
+  @retval EFI_DEVICE_ERROR       Failed to create a NULL UDP port to keep\r
+                                 connection  with UDP.\r
+  @retval EFI_SUCCESS            The service instance is created for the\r
+                                 controller.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4CreateService (\r
+  IN  EFI_HANDLE            Controller,\r
+  IN  EFI_HANDLE            Image,\r
+  OUT MTFTP4_SERVICE        **Service\r
+  )\r
+{\r
+  MTFTP4_SERVICE            *MtftpSb;\r
+  EFI_STATUS                Status;\r
+\r
+  *Service  = NULL;\r
+  MtftpSb   = NetAllocatePool (sizeof (MTFTP4_SERVICE));\r
+\r
+  if (MtftpSb == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  MtftpSb->Signature      = MTFTP4_SERVICE_SIGNATURE;\r
+  MtftpSb->ServiceBinding = gMtftp4ServiceBindingTemplete;\r
+  MtftpSb->InDestory      = FALSE;\r
+  MtftpSb->ChildrenNum    = 0;\r
+  NetListInit (&MtftpSb->Children);\r
+\r
+  MtftpSb->Timer          = NULL;\r
+  MtftpSb->TimerToGetMap  = NULL;\r
+  MtftpSb->Controller     = Controller;\r
+  MtftpSb->Image          = Image;\r
+  MtftpSb->ConnectUdp     = NULL;\r
+\r
+  //\r
+  // Create the timer and a udp to be notified when UDP is uninstalled\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  Mtftp4OnTimerTick,\r
+                  MtftpSb,\r
+                  &MtftpSb->Timer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (MtftpSb);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Create the timer used to time out the procedure which is used to\r
+  // get the default IP address.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &MtftpSb->TimerToGetMap\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseEvent (MtftpSb->Timer);\r
+    NetFreePool (MtftpSb);\r
+    return Status;\r
+  }\r
+\r
+  MtftpSb->ConnectUdp = UdpIoCreatePort (Controller, Image, Mtftp4ConfigNullUdp, NULL);\r
+\r
+  if (MtftpSb->ConnectUdp == NULL) {\r
+    gBS->CloseEvent (MtftpSb->TimerToGetMap);\r
+    gBS->CloseEvent (MtftpSb->Timer);\r
+    NetFreePool (MtftpSb);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  *Service = MtftpSb;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Release all the resource used the MTFTP service binding instance.\r
+\r
+  @param  MtftpSb                The MTFTP service binding instance.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Mtftp4CleanService (\r
+  IN MTFTP4_SERVICE     *MtftpSb\r
+  )\r
+{\r
+  UdpIoFreePort (MtftpSb->ConnectUdp);\r
+  gBS->CloseEvent (MtftpSb->TimerToGetMap);\r
+  gBS->CloseEvent (MtftpSb->Timer);\r
+}\r
+\r
+\r
+/**\r
+  Start the MTFTP driver on this controller. MTFTP driver will\r
+  install a MTFTP SERVICE BINDING protocol on the supported\r
+  controller, which can be used to create/destroy MTFTP children.\r
+\r
+  @param  This                   The MTFTP driver binding protocol.\r
+  @param  Controller             The controller to manage.\r
+  @param  RemainingDevicePath    Remaining device path.\r
+\r
+  @retval EFI_ALREADY_STARTED    The MTFTP service binding protocol has been\r
+                                 started  on the controller.\r
+  @retval EFI_SUCCESS            The MTFTP service binding is installed on the\r
+                                 controller.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
+  )\r
+{\r
+  MTFTP4_SERVICE            *MtftpSb;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Directly return if driver is already running.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiMtftp4ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  Status = Mtftp4CreateService (Controller, This->DriverBindingHandle, &MtftpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Install the Mtftp4ServiceBinding Protocol onto Controller\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Controller,\r
+                  &gEfiMtftp4ServiceBindingProtocolGuid,\r
+                  &MtftpSb->ServiceBinding,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  Mtftp4CleanService (MtftpSb);\r
+  NetFreePool (MtftpSb);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop the MTFTP driver on controller. The controller is a UDP\r
+  child handle.\r
+\r
+  @param  This                   The MTFTP driver binding protocol\r
+  @param  Controller             The controller to stop\r
+  @param  NumberOfChildren       The number of children\r
+  @param  ChildHandleBuffer      The array of the child handle.\r
+\r
+  @retval EFI_SUCCESS            The driver is stopped on the controller.\r
+  @retval EFI_DEVICE_ERROR       Failed to stop the driver on the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN  EFI_HANDLE                  Controller,\r
+  IN  UINTN                       NumberOfChildren,\r
+  IN  EFI_HANDLE                  *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  MTFTP4_SERVICE                *MtftpSb;\r
+  MTFTP4_PROTOCOL               *Instance;\r
+  EFI_HANDLE                    NicHandle;\r
+  EFI_STATUS                    Status;\r
+  EFI_TPL                       OldTpl;\r
+\r
+  //\r
+  // MTFTP driver opens UDP child, So, Controller is a UDP\r
+  // child handle. Locate the Nic handle first. Then get the\r
+  // MTFTP private data back.\r
+  //\r
+  NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp4ProtocolGuid);\r
+\r
+  if (NicHandle == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  &gEfiMtftp4ServiceBindingProtocolGuid,\r
+                  (VOID **) &ServiceBinding,\r
+                  This->DriverBindingHandle,\r
+                  NicHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  MtftpSb = MTFTP4_SERVICE_FROM_THIS (ServiceBinding);\r
+\r
+  if (MtftpSb->InDestory) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  OldTpl             = NET_RAISE_TPL (NET_TPL_LOCK);\r
+  MtftpSb->InDestory = TRUE;\r
+\r
+  while (!NetListIsEmpty (&MtftpSb->Children)) {\r
+    Instance = NET_LIST_HEAD (&MtftpSb->Children, MTFTP4_PROTOCOL, Link);\r
+    Mtftp4ServiceBindingDestroyChild (ServiceBinding, Instance->Handle);\r
+  }\r
+\r
+  if (MtftpSb->ChildrenNum != 0) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  NicHandle,\r
+                  &gEfiMtftp4ServiceBindingProtocolGuid,\r
+                  ServiceBinding\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Mtftp4CleanService (MtftpSb);\r
+  NetFreePool (MtftpSb);\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  MtftpSb->InDestory = FALSE;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Initialize a MTFTP protocol instance which is the child of MtftpSb.\r
+\r
+  @param  MtftpSb                The MTFTP service binding protocol.\r
+  @param  Instance               The MTFTP instance to initialize.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Mtftp4InitProtocol (\r
+  IN MTFTP4_SERVICE         *MtftpSb,\r
+  IN MTFTP4_PROTOCOL        *Instance\r
+  )\r
+{\r
+  NetZeroMem (Instance, sizeof (MTFTP4_PROTOCOL));\r
+\r
+  Instance->Signature = MTFTP4_PROTOCOL_SIGNATURE;\r
+  NetListInit (&Instance->Link);\r
+  CopyMem (&Instance->Mtftp4, &gMtftp4ProtocolTemplate, sizeof (EFI_MTFTP4_PROTOCOL));\r
+  Instance->State     = MTFTP4_STATE_UNCONFIGED;\r
+  Instance->Indestory = FALSE;\r
+  Instance->Service   = MtftpSb;\r
+\r
+  NetListInit (&Instance->Blocks);\r
+}\r
+\r
+\r
+/**\r
+  Create a MTFTP child for the service binding instance, then\r
+  install the MTFTP protocol to the ChildHandle.\r
+\r
+  @param  This                   The MTFTP service binding instance.\r
+  @param  ChildHandle            The Child handle to install the MTFTP protocol.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource for the new child.\r
+  @retval EFI_SUCCESS            The child is successfully create.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4ServiceBindingCreateChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN OUT EFI_HANDLE                *ChildHandle\r
+  )\r
+{\r
+  MTFTP4_SERVICE            *MtftpSb;\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  VOID                      *Udp4;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = NetAllocatePool (sizeof (*Instance));\r
+\r
+  if (Instance == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);\r
+\r
+  Mtftp4InitProtocol (MtftpSb, Instance);\r
+\r
+  Instance->UnicastPort = UdpIoCreatePort (\r
+                            MtftpSb->Controller,\r
+                            MtftpSb->Image,\r
+                            Mtftp4ConfigNullUdp,\r
+                            Instance\r
+                            );\r
+\r
+  if (Instance->UnicastPort == NULL) {\r
+    NetFreePool (Instance);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Install the MTFTP protocol onto ChildHandle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiMtftp4ProtocolGuid,\r
+                  &Instance->Mtftp4,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Instance->Handle  = *ChildHandle;\r
+\r
+  //\r
+  // Open the Udp4 protocol BY_CHILD.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  MtftpSb->ConnectUdp->UdpHandle,\r
+                  &gEfiUdp4ProtocolGuid,\r
+                  (VOID **) &Udp4,\r
+                  gMtftp4DriverBinding.DriverBindingHandle,\r
+                  Instance->Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Instance->Handle,\r
+           &gEfiMtftp4ProtocolGuid,\r
+           &Instance->Mtftp4,\r
+           NULL\r
+           );\r
+\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Add it to the parent's child list.\r
+  //\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  NetListInsertTail (&MtftpSb->Children, &Instance->Link);\r
+  MtftpSb->ChildrenNum++;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+ON_ERROR:\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    UdpIoFreePort (Instance->UnicastPort);\r
+    NetFreePool (Instance);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Destory one of the service binding's child.\r
+\r
+  @param  This                   The service binding instance\r
+  @param  ChildHandle            The child handle to destory\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invaid.\r
+  @retval EFI_UNSUPPORTED        The child may have already been destoried.\r
+  @retval EFI_SUCCESS            The child is destoried and removed from the\r
+                                 parent's child list.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                   ChildHandle\r
+  )\r
+{\r
+  MTFTP4_SERVICE            *MtftpSb;\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_MTFTP4_PROTOCOL       *Mtftp4;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Retrieve the private context data structures\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiMtftp4ProtocolGuid,\r
+                  (VOID **) &Mtftp4,\r
+                  gMtftp4DriverBinding.DriverBindingHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Instance  = MTFTP4_PROTOCOL_FROM_THIS (Mtftp4);\r
+  MtftpSb   = MTFTP4_SERVICE_FROM_THIS (This);\r
+\r
+  if (Instance->Service != MtftpSb) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Instance->Indestory) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Instance->Indestory = TRUE;\r
+\r
+  //\r
+  // Close the Udp4 protocol.\r
+  //\r
+  gBS->CloseProtocol (\r
+         MtftpSb->ConnectUdp->UdpHandle,\r
+         &gEfiUdp4ProtocolGuid,\r
+         gMtftp4DriverBinding.DriverBindingHandle,\r
+         ChildHandle\r
+         );\r
+\r
+  //\r
+  // Uninstall the MTFTP4 protocol first to enable a top down destruction.\r
+  //\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  ChildHandle,\r
+                  &gEfiMtftp4ProtocolGuid,\r
+                  Mtftp4\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Instance->Indestory = FALSE;\r
+    return Status;\r
+  }\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  Mtftp4CleanOperation (Instance, EFI_DEVICE_ERROR);\r
+  UdpIoFreePort (Instance->UnicastPort);\r
+\r
+  NetListRemoveEntry (&Instance->Link);\r
+  MtftpSb->ChildrenNum--;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  NetFreePool (Instance);\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h
new file mode 100644 (file)
index 0000000..a9b7ac7
--- /dev/null
@@ -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 (file)
index 0000000..d4198c5
--- /dev/null
@@ -0,0 +1,68 @@
+#/** @file\r
+# Component name for module Mtftp4\r
+#\r
+# Copyright (c) 2007, Intel Corporation.\r
+#\r
+#  All rights reserved. This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution. The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = Mtftp4Dxe\r
+  FILE_GUID                      = DC3641B8-2FA8-4ed3-BC1F-F9962A03454B\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  EDK_RELEASE_VERSION            = 0x00020000\r
+  EFI_SPECIFICATION_VERSION      = 0x00020000\r
+\r
+  ENTRY_POINT                    = Mtftp4DriverEntryPoint\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources.common]\r
+  Mtftp4Option.c\r
+  Mtftp4Rrq.c\r
+  Mtftp4Impl.h\r
+  ComponentName.c\r
+  Mtftp4Support.c\r
+  Mtftp4Impl.c\r
+  Mtftp4Option.h\r
+  Mtftp4Support.h\r
+  Mtftp4Driver.h\r
+  Mtftp4Driver.c\r
+  Mtftp4Wrq.c\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  UefiLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  DebugLib\r
+  NetLib\r
+  UdpIoLib\r
+\r
+\r
+[Protocols]\r
+  gEfiMtftp4ServiceBindingProtocolGuid          # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiMtftp4ProtocolGuid                        # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiUdp4ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiUdp4ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED\r
+\r
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa
new file mode 100644 (file)
index 0000000..c7b8360
--- /dev/null
@@ -0,0 +1,79 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+  <MsaHeader>\r
+    <ModuleName>Mtftp4Dxe</ModuleName>\r
+    <ModuleType>DXE_DRIVER</ModuleType>\r
+    <GuidValue>DC3641B8-2FA8-4ed3-BC1F-F9962A03454B</GuidValue>\r
+    <Version>1.0</Version>\r
+    <Abstract>Component name for module Mtftp4</Abstract>\r
+    <Description>FIX ME!</Description>\r
+    <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>\r
+    <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>\r
+    <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION   0x00000052</Specification>\r
+  </MsaHeader>\r
+  <ModuleDefinitions>\r
+    <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>\r
+    <BinaryModule>false</BinaryModule>\r
+    <OutputFileBasename>Mtftp4Dxe</OutputFileBasename>\r
+  </ModuleDefinitions>\r
+  <LibraryClassDefinitions>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>DebugLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiDriverEntryPoint</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiBootServicesTableLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>BaseLib</Keyword>\r
+    </LibraryClass>\r
+  </LibraryClassDefinitions>\r
+  <SourceFiles>\r
+    <Filename>Mtftp4Wrq.c</Filename>\r
+    <Filename>Mtftp4Driver.c</Filename>\r
+    <Filename>Mtftp4Driver.h</Filename>\r
+    <Filename>Mtftp4Support.h</Filename>\r
+    <Filename>Mtftp4Option.h</Filename>\r
+    <Filename>Mtftp4Impl.c</Filename>\r
+    <Filename>Mtftp4Support.c</Filename>\r
+    <Filename>ComponentName.c</Filename>\r
+    <Filename>Mtftp4Impl.h</Filename>\r
+    <Filename>Mtftp4Rrq.c</Filename>\r
+    <Filename>Mtftp4Option.c</Filename>\r
+  </SourceFiles>\r
+  <PackageDependencies>\r
+    <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>\r
+    <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>\r
+  </PackageDependencies>\r
+  <Protocols>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiUdp4ProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiUdp4ServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiMtftp4ProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiMtftp4ServiceBindingProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+  </Protocols>\r
+  <Externs>\r
+    <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>\r
+    <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>\r
+    <Extern>\r
+      <ModuleEntryPoint>Mtftp4DriverEntryPoint</ModuleEntryPoint>\r
+    </Extern>\r
+  </Externs>\r
+</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 (file)
index 0000000..5bfd253
--- /dev/null
@@ -0,0 +1,892 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Mtftp4Impl.c\r
+\r
+Abstract:\r
+\r
+  Interface routine for Mtftp4\r
+\r
+\r
+**/\r
+\r
+#include "Mtftp4Impl.h"\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4ReadFile (\r
+  IN EFI_MTFTP4_PROTOCOL    *This,\r
+  IN EFI_MTFTP4_TOKEN       *Token\r
+  );\r
+\r
+\r
+/**\r
+  Get the current operation parameter for the MTFTP session\r
+\r
+  @param  This                   The MTFTP protocol instance\r
+  @param  ModeData               The MTFTP mode data\r
+\r
+  @retval EFI_INVALID_PARAMETER  This or ModeData is NULL\r
+  @retval EFI_SUCCESS            The operation parameter is saved in ModeData\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4GetModeData (\r
+  IN EFI_MTFTP4_PROTOCOL    *This,\r
+  OUT EFI_MTFTP4_MODE_DATA  *ModeData\r
+  )\r
+{\r
+  MTFTP4_PROTOCOL  *Instance;\r
+  EFI_TPL          OldTpl;\r
+\r
+  if ((This == NULL) || (ModeData == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  Instance                         = MTFTP4_PROTOCOL_FROM_THIS (This);\r
+  ModeData->ConfigData             = Instance->Config;\r
+  ModeData->SupportedOptionCount   = MTFTP4_SUPPORTED_OPTIONS;\r
+  ModeData->SupportedOptoins       = mMtftp4SupportedOptions;\r
+  ModeData->UnsupportedOptionCount = 0;\r
+  ModeData->UnsupportedOptoins     = NULL;\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Clean up the MTFTP session to get ready for new operation.\r
+\r
+  @param  Instance               The MTFTP session to clean up\r
+  @param  Result                 The result to return to the caller who initiated\r
+                                 the operation.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Mtftp4CleanOperation (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN EFI_STATUS             Result\r
+  )\r
+{\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  MTFTP4_BLOCK_RANGE        *Block;\r
+  EFI_MTFTP4_TOKEN          *Token;\r
+\r
+  //\r
+  // Free various resources.\r
+  //\r
+  Token = Instance->Token;\r
+\r
+  if (Token != NULL) {\r
+    Token->Status = Result;\r
+\r
+    if (Token->Event != NULL) {\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+\r
+    Instance->Token = NULL;\r
+  }\r
+\r
+  ASSERT (Instance->UnicastPort != NULL);\r
+  UdpIoCleanPort (Instance->UnicastPort);\r
+\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+    Instance->LastPacket = NULL;\r
+  }\r
+\r
+  if (Instance->McastUdpPort != NULL) {\r
+    UdpIoFreePort (Instance->McastUdpPort);\r
+    Instance->McastUdpPort = NULL;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->Blocks) {\r
+    Block = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);\r
+    NetListRemoveEntry (Entry);\r
+    NetFreePool (Block);\r
+  }\r
+\r
+  NetZeroMem (&Instance->RequestOption, sizeof (MTFTP4_OPTION));\r
+\r
+  Instance->Operation     = 0;\r
+\r
+  Instance->BlkSize       = MTFTP4_DEFAULT_BLKSIZE;\r
+  Instance->LastBlock     = 0;\r
+  Instance->ServerIp      = 0;\r
+  Instance->ListeningPort = 0;\r
+  Instance->ConnectedPort = 0;\r
+  Instance->Gateway       = 0;\r
+  Instance->PacketToLive  = 0;\r
+  Instance->MaxRetry      = 0;\r
+  Instance->CurRetry      = 0;\r
+  Instance->Timeout       = 0;\r
+  Instance->McastIp       = 0;\r
+  Instance->McastPort     = 0;\r
+  Instance->Master        = TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Configure the MTFTP session for new operation or reset the current\r
+  operation if ConfigData is NULL.\r
+\r
+  @param  This                   The MTFTP session to configure\r
+  @param  ConfigData             The configure parameters\r
+\r
+  @retval EFI_INVALID_PARAMETER  Some of the parameter is invalid.\r
+  @retval EFI_ACCESS_DENIED      There is pending operation\r
+  @retval EFI_SUCCESS            The instance is configured for operation.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4Configure (\r
+  IN EFI_MTFTP4_PROTOCOL    *This,\r
+  IN EFI_MTFTP4_CONFIG_DATA *ConfigData\r
+  )\r
+{\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_TPL                   OldTpl;\r
+  IP4_ADDR                  Ip;\r
+  IP4_ADDR                  Netmask;\r
+  IP4_ADDR                  Gateway;\r
+  IP4_ADDR                  ServerIp;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = MTFTP4_PROTOCOL_FROM_THIS (This);\r
+\r
+  if (ConfigData == NULL) {\r
+    //\r
+    // Reset the operation if ConfigData is NULL\r
+    //\r
+    OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+    Mtftp4CleanOperation (Instance, EFI_ABORTED);\r
+    NetZeroMem (&Instance->Config, sizeof (EFI_MTFTP4_CONFIG_DATA));\r
+    Instance->State = MTFTP4_STATE_UNCONFIGED;\r
+\r
+    NET_RESTORE_TPL (OldTpl);\r
+\r
+  } else {\r
+    //\r
+    // Configure the parameters for new operation.\r
+    //\r
+    NetCopyMem (&Ip, &ConfigData->StationIp, sizeof (IP4_ADDR));\r
+    NetCopyMem (&Netmask, &ConfigData->SubnetMask, sizeof (IP4_ADDR));\r
+    NetCopyMem (&Gateway, &ConfigData->GatewayIp, sizeof (IP4_ADDR));\r
+    NetCopyMem (&ServerIp, &ConfigData->ServerIp, sizeof (IP4_ADDR));\r
+\r
+    Ip       = NTOHL (Ip);\r
+    Netmask  = NTOHL (Netmask);\r
+    Gateway  = NTOHL (Gateway);\r
+    ServerIp = NTOHL (ServerIp);\r
+\r
+    if (!Ip4IsUnicast (ServerIp, 0)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (!ConfigData->UseDefaultSetting &&\r
+       ((!IP4_IS_VALID_NETMASK (Netmask) || !Ip4IsUnicast (Ip, Netmask)))) {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if ((Gateway != 0) &&\r
+       (!IP4_NET_EQUAL (Gateway, Ip, Netmask) || !Ip4IsUnicast (Gateway, Netmask))) {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+    if ((Instance->State == MTFTP4_STATE_CONFIGED) && (Instance->Operation != 0)) {\r
+      NET_RESTORE_TPL (OldTpl);\r
+      return EFI_ACCESS_DENIED;\r
+    }\r
+\r
+    Instance->Config = *ConfigData;\r
+    Instance->State = MTFTP4_STATE_CONFIGED;\r
+\r
+    NET_RESTORE_TPL (OldTpl);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Check packet for GetInfo. GetInfo is implemented with EfiMtftp4ReadFile.\r
+  It use Mtftp4GetInfoCheckPacket to inspect the first packet from server,\r
+  then abort the session.\r
+\r
+  @param  This                   The MTFTP4 protocol instance\r
+  @param  Token                  The user's token\r
+  @param  PacketLen              The length of the packet\r
+  @param  Packet                 The received packet.\r
+\r
+  @retval EFI_ABORTED            Abort the ReadFile operation and return.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4GetInfoCheckPacket (\r
+  IN EFI_MTFTP4_PROTOCOL    *This,\r
+  IN EFI_MTFTP4_TOKEN       *Token,\r
+  IN UINT16                 PacketLen,\r
+  IN EFI_MTFTP4_PACKET      *Packet\r
+  )\r
+{\r
+  MTFTP4_GETINFO_STATE      *State;\r
+  EFI_STATUS                Status;\r
+  UINT16                    OpCode;\r
+\r
+  State   = (MTFTP4_GETINFO_STATE *) Token->Context;\r
+  OpCode  = NTOHS (Packet->OpCode);\r
+\r
+  //\r
+  // Set the GetInfo's return status according to the OpCode.\r
+  //\r
+  switch (OpCode) {\r
+  case EFI_MTFTP4_OPCODE_ERROR:\r
+    State->Status = EFI_TFTP_ERROR;\r
+    break;\r
+\r
+  case EFI_MTFTP4_OPCODE_OACK:\r
+    State->Status = EFI_SUCCESS;\r
+    break;\r
+\r
+  default:\r
+    State->Status = EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // Allocate buffer then copy the packet over. Use gBS->AllocatePool\r
+  // in case NetAllocatePool will implements something tricky.\r
+  //\r
+  Status = gBS->AllocatePool (EfiBootServicesData, PacketLen, (VOID **) State->Packet);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    State->Status = EFI_OUT_OF_RESOURCES;\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  *(State->PacketLen) = PacketLen;\r
+  NetCopyMem (*(State->Packet), Packet, PacketLen);\r
+\r
+  return EFI_ABORTED;\r
+}\r
+\r
+\r
+/**\r
+  Get the information of the download from the server. It is implemented\r
+  with EfiMtftp4ReadFile: build a token, then pass it to EfiMtftp4ReadFile.\r
+  In its check packet callback abort the opertions.\r
+\r
+  @param  This                   The MTFTP protocol instance\r
+  @param  OverrideData           The MTFTP override data\r
+  @param  Filename               The file to get information\r
+  @param  ModeStr                The mode to use\r
+  @param  OptionCount            The number of options to append\r
+  @param  OptionList             The options to append\r
+  @param  PacketLength           The variable to receive the packet length\r
+  @param  Packet                 The variable to receive the packet.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invaid\r
+  @retval EFI_SUCCESS            The information is got\r
+  @retval Others                 Failed to get the information.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4GetInfo (\r
+  IN EFI_MTFTP4_PROTOCOL      *This,\r
+  IN EFI_MTFTP4_OVERRIDE_DATA *OverrideData,        OPTIONAL\r
+  IN UINT8                    *Filename,\r
+  IN UINT8                    *ModeStr,             OPTIONAL\r
+  IN UINT8                    OptionCount,\r
+  IN EFI_MTFTP4_OPTION        *OptionList,\r
+  OUT UINT32                  *PacketLength,\r
+  OUT EFI_MTFTP4_PACKET       **Packet OPTIONAL\r
+  )\r
+{\r
+  EFI_MTFTP4_TOKEN          Token;\r
+  MTFTP4_GETINFO_STATE      State;\r
+  EFI_STATUS                Status;\r
+\r
+  if ((This == NULL) || (Filename == NULL) || (PacketLength == NULL) ||\r
+      (OptionCount && (OptionList == NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Packet != NULL) {\r
+    *Packet = NULL;\r
+  }\r
+\r
+  *PacketLength         = 0;\r
+  State.Packet          = Packet;\r
+  State.PacketLen       = PacketLength;\r
+  State.Status          = EFI_SUCCESS;\r
+\r
+  //\r
+  // Fill in the Token to issue an synchronous ReadFile operation\r
+  //\r
+  Token.Status          = EFI_SUCCESS;\r
+  Token.Event           = NULL;\r
+  Token.OverrideData    = OverrideData;\r
+  Token.Filename        = Filename;\r
+  Token.ModeStr         = ModeStr;\r
+  Token.OptionCount     = OptionCount;\r
+  Token.OptionList      = OptionList;\r
+  Token.BufferSize      = 0;\r
+  Token.Buffer          = NULL;\r
+  Token.Context         = &State;\r
+  Token.CheckPacket     = Mtftp4GetInfoCheckPacket;\r
+  Token.TimeoutCallback = NULL;\r
+  Token.PacketNeeded    = NULL;\r
+\r
+  Status                = EfiMtftp4ReadFile (This, &Token);\r
+\r
+  if (EFI_ABORTED == Status) {\r
+    return State.Status;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Parse the packet into an array of options. The OptionList is allocated\r
+  by this function, and caller should free it when used.\r
+\r
+  @param  This                   The MTFTP protocol instance\r
+  @param  PacketLen              The length of the packet\r
+  @param  Packet                 The packet to parse\r
+  @param  OptionCount            The size of the OptionList array allocated.\r
+  @param  OptionList             The allocated option array to save the option\r
+                                 addresses.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameters are invalid.\r
+  @retval EFI_NOT_FOUND          There is no valid option in the packet\r
+  @retval EFI_SUCCESS            The packet is parsed.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4ParseOptions (\r
+  IN EFI_MTFTP4_PROTOCOL    *This,\r
+  IN UINT32                 PacketLen,\r
+  IN EFI_MTFTP4_PACKET      *Packet,\r
+  IN OUT UINT32             *OptionCount,\r
+  OUT EFI_MTFTP4_OPTION     **OptionList          OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  if ((This == NULL) || (PacketLen < MTFTP4_OPCODE_LEN) ||\r
+      (Packet == NULL) || (OptionCount == NULL)) {\r
+\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = Mtftp4ExtractOptions (Packet, PacketLen, OptionCount, OptionList);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (*OptionCount == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Check whether the override data is valid. It will first\r
+  validate whether the server is a valid unicast. If a gateway\r
+  is provided in the Override, it also check that it is a\r
+  unicast on the connected network.\r
+\r
+  @param  Instance               The MTFTP instance\r
+  @param  Override               The override data to validate.\r
+\r
+  @return TRUE if the override data is valid, otherwise FALSE.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+Mtftp4OverrideValid (\r
+  IN MTFTP4_PROTOCOL          *Instance,\r
+  IN EFI_MTFTP4_OVERRIDE_DATA *Override\r
+  )\r
+{\r
+  EFI_MTFTP4_CONFIG_DATA    *Config;\r
+  IP4_ADDR                  Ip;\r
+  IP4_ADDR                  Netmask;\r
+  IP4_ADDR                  Gateway;\r
+\r
+  NetCopyMem (&Ip, &Override->ServerIp, sizeof (IP4_ADDR));\r
+  if (!Ip4IsUnicast (NTOHL (Ip), 0)) {\r
+    return FALSE;\r
+  }\r
+\r
+  Config = &Instance->Config;\r
+\r
+  NetCopyMem (&Gateway, &Override->GatewayIp, sizeof (IP4_ADDR));\r
+  Gateway = NTOHL (Gateway);\r
+\r
+  if (!Config->UseDefaultSetting && (Gateway != 0)) {\r
+    NetCopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR));\r
+    NetCopyMem (&Ip, &Config->StationIp, sizeof (IP4_ADDR));\r
+\r
+    Netmask = NTOHL (Netmask);\r
+    Ip      = NTOHL (Ip);\r
+\r
+    if (!Ip4IsUnicast (Gateway, Netmask) || !IP4_NET_EQUAL (Gateway, Ip, Netmask)) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Poll the UDP to get the IP4 default address, which may be retrieved\r
+  by DHCP. The default time out value is 5 seconds. If IP has retrieved\r
+  the default address, the UDP is reconfigured.\r
+\r
+  @param  Instance               The Mtftp instance\r
+  @param  UdpPort                The UDP port to poll\r
+  @param  UdpCfgData             The UDP configure data to reconfigure the UDP\r
+                                 port.\r
+\r
+  @return TRUE if the default address is retrieved and UDP is reconfigured.\r
+  @return Otherwise FALSE.\r
+\r
+**/\r
+BOOLEAN\r
+Mtftp4GetMapping (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN UDP_IO_PORT            *UdpPort,\r
+  IN EFI_UDP4_CONFIG_DATA   *UdpCfgData\r
+  )\r
+{\r
+  MTFTP4_SERVICE            *Service;\r
+  EFI_IP4_MODE_DATA         Ip4Mode;\r
+  EFI_UDP4_PROTOCOL         *Udp;\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT (Instance->Config.UseDefaultSetting);\r
+\r
+  Service = Instance->Service;\r
+  Udp     = UdpPort->Udp;\r
+\r
+  Status = gBS->SetTimer (\r
+                  Service->TimerToGetMap,\r
+                  TimerRelative,\r
+                  MTFTP4_TIME_TO_GETMAP * TICKS_PER_SECOND\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  while (!EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) {\r
+    Udp->Poll (Udp);\r
+\r
+    if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip4Mode, NULL, NULL)) &&\r
+        Ip4Mode.IsConfigured) {\r
+\r
+      Udp->Configure (Udp, NULL);\r
+      return (BOOLEAN) (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS);\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  Configure the UDP port for unicast receiving.\r
+\r
+  @param  UdpIo                  The UDP port\r
+  @param  Instance               The MTFTP session\r
+\r
+  @retval EFI_SUCCESS            The UDP port is successfully configured for the\r
+                                 session to unicast receive.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4ConfigUnicastPort (\r
+  IN UDP_IO_PORT            *UdpIo,\r
+  IN MTFTP4_PROTOCOL        *Instance\r
+  )\r
+{\r
+  EFI_MTFTP4_CONFIG_DATA    *Config;\r
+  EFI_UDP4_CONFIG_DATA      UdpConfig;\r
+  EFI_STATUS                Status;\r
+  IP4_ADDR                  Ip;\r
+\r
+  Config = &Instance->Config;\r
+\r
+  UdpConfig.AcceptBroadcast    = FALSE;\r
+  UdpConfig.AcceptPromiscuous  = FALSE;\r
+  UdpConfig.AcceptAnyPort      = FALSE;\r
+  UdpConfig.AllowDuplicatePort = FALSE;\r
+  UdpConfig.TypeOfService      = 0;\r
+  UdpConfig.TimeToLive         = 64;\r
+  UdpConfig.DoNotFragment      = FALSE;\r
+  UdpConfig.ReceiveTimeout     = 0;\r
+  UdpConfig.TransmitTimeout    = 0;\r
+  UdpConfig.UseDefaultAddress  = Config->UseDefaultSetting;\r
+  UdpConfig.StationAddress     = Config->StationIp;\r
+  UdpConfig.SubnetMask         = Config->SubnetMask;\r
+  UdpConfig.StationPort        = 0;\r
+  UdpConfig.RemotePort         = 0;\r
+\r
+  Ip = HTONL (Instance->ServerIp);\r
+  NetCopyMem (&UdpConfig.RemoteAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Status = UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfig);\r
+\r
+  if ((Status == EFI_NO_MAPPING) && Mtftp4GetMapping (Instance, UdpIo, &UdpConfig)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start the MTFTP session to do the operation, such as read file,\r
+  write file, and read directory.\r
+\r
+  @param  This                   The MTFTP session\r
+  @param  Token                  The token than encapsues the user's request.\r
+  @param  Operation              The operation to do\r
+\r
+  @retval EFI_INVALID_PARAMETER  Some of the parameters are invalid.\r
+  @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.\r
+  @retval EFI_ALREADY_STARTED    There is pending operation for the session.\r
+  @retval EFI_SUCCESS            The operation is successfully started.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4Start (\r
+  IN EFI_MTFTP4_PROTOCOL    *This,\r
+  IN EFI_MTFTP4_TOKEN       *Token,\r
+  IN UINT16                 Operation\r
+  )\r
+{\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_MTFTP4_OVERRIDE_DATA  *Override;\r
+  EFI_MTFTP4_CONFIG_DATA    *Config;\r
+  EFI_TPL                   OldTpl;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Validate the parameters\r
+  //\r
+  if ((This == NULL) || (Token == NULL) || (Token->Filename == NULL) ||\r
+      ((Token->OptionCount != 0) && (Token->OptionList == NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // User must provide at least one method to collect the data for download.\r
+  //\r
+  if (((Operation == EFI_MTFTP4_OPCODE_RRQ) || (Operation == EFI_MTFTP4_OPCODE_DIR)) &&\r
+      ((Token->Buffer == NULL) && (Token->CheckPacket == NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // User must provide at least one method to provide the data for upload.\r
+  //\r
+  if ((Operation == EFI_MTFTP4_OPCODE_WRQ) &&\r
+     ((Token->Buffer == NULL) && (Token->PacketNeeded == NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = MTFTP4_PROTOCOL_FROM_THIS (This);\r
+\r
+  Status = EFI_SUCCESS;\r
+  OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);\r
+\r
+  if (Instance->State != MTFTP4_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Instance->Operation != 0) {\r
+    Status = EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NET_RESTORE_TPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Set the Operation now to prevent the application start other\r
+  // operations.\r
+  //\r
+  Instance->Operation = Operation;\r
+  Override            = Token->OverrideData;\r
+\r
+  if ((Override != NULL) && !Mtftp4OverrideValid (Instance, Override)) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (Token->OptionCount != 0) {\r
+    Status = Mtftp4ParseOption (\r
+               Token->OptionList,\r
+               Token->OptionCount,\r
+               TRUE,\r
+               &Instance->RequestOption\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Set the operation parameters from the configuration or override data.\r
+  //\r
+  Config                  = &Instance->Config;\r
+  Instance->Token         = Token;\r
+  Instance->BlkSize       = MTFTP4_DEFAULT_BLKSIZE;\r
+\r
+  NetCopyMem (&Instance->ServerIp, &Config->ServerIp, sizeof (IP4_ADDR));\r
+  Instance->ServerIp      = NTOHL (Instance->ServerIp);\r
+\r
+  Instance->ListeningPort = Config->InitialServerPort;\r
+  Instance->ConnectedPort = 0;\r
+\r
+  NetCopyMem (&Instance->Gateway, &Config->GatewayIp, sizeof (IP4_ADDR));\r
+  Instance->Gateway       = NTOHL (Instance->Gateway);\r
+\r
+  Instance->MaxRetry      = Config->TryCount;\r
+  Instance->Timeout       = Config->TimeoutValue;\r
+  Instance->Master        = TRUE;\r
+\r
+  if (Override != NULL) {\r
+    NetCopyMem (&Instance->ServerIp, &Override->ServerIp, sizeof (IP4_ADDR));\r
+    NetCopyMem (&Instance->Gateway, &Override->GatewayIp, sizeof (IP4_ADDR));\r
+\r
+    Instance->ServerIp      = NTOHL (Instance->ServerIp);\r
+    Instance->Gateway       = NTOHL (Instance->Gateway);\r
+\r
+    Instance->ListeningPort = Override->ServerPort;\r
+    Instance->MaxRetry      = Override->TryCount;\r
+    Instance->Timeout       = Override->TimeoutValue;\r
+  }\r
+\r
+  if (Instance->ListeningPort == 0) {\r
+    Instance->ListeningPort = MTFTP4_DEFAULT_SERVER_PORT;\r
+  }\r
+\r
+  if (Instance->MaxRetry == 0) {\r
+    Instance->MaxRetry = MTFTP4_DEFAULT_RETRY;\r
+  }\r
+\r
+  if (Instance->Timeout == 0) {\r
+    Instance->Timeout = MTFTP4_DEFAULT_TIMEOUT;\r
+  }\r
+\r
+  //\r
+  // Config the unicast UDP child to send initial request\r
+  //\r
+  Status = Mtftp4ConfigUnicastPort (Instance->UnicastPort, Instance);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Build and send an initial requests\r
+  //\r
+  if (Operation == EFI_MTFTP4_OPCODE_WRQ) {\r
+    Status = Mtftp4WrqStart (Instance, Operation);\r
+  } else {\r
+    Status = Mtftp4RrqStart (Instance, Operation);\r
+  }\r
+\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+  //\r
+  // Return immediately for asynchronous operation or poll the\r
+  // instance for synchronous operation.\r
+  //\r
+  Token->Status = EFI_NOT_READY;\r
+\r
+  if (Token->Event != NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  while (Token->Status == EFI_NOT_READY) {\r
+    This->Poll (This);\r
+  }\r
+\r
+  return Token->Status;\r
+\r
+ON_ERROR:\r
+  Mtftp4CleanOperation (Instance, Status);\r
+  NET_RESTORE_TPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Read a file from the server.\r
+\r
+  @param  This                   The Mtftp protocol instance.\r
+  @param  Token                  The user's request wrap token.\r
+\r
+  @retval EFI_SUCCESS            The ReadFile has finished, the file has been\r
+                                 downloaded if it is synchronous operation,\r
+                                 otherwise it has been  initated.\r
+  @retval Others                 Some error happened.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4ReadFile (\r
+  IN EFI_MTFTP4_PROTOCOL    *This,\r
+  IN EFI_MTFTP4_TOKEN       *Token\r
+  )\r
+{\r
+  return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_RRQ);\r
+}\r
+\r
+\r
+/**\r
+  Upload a file to the server.\r
+\r
+  @param  This                   The MTFTP protocol session\r
+  @param  Token                  The user's request wrap token.\r
+\r
+  @retval EFI_SUCCESS            The WriteFile has finished, the file has been\r
+                                 uploaded if it is synchronous operation, otherwise\r
+                                 it has been  initated.\r
+  @retval Others                 Some error happened.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4WriteFile (\r
+  IN EFI_MTFTP4_PROTOCOL    *This,\r
+  IN EFI_MTFTP4_TOKEN       *Token\r
+  )\r
+{\r
+  return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_WRQ);\r
+}\r
+\r
+\r
+/**\r
+  Read a directory from the server. The only difference\r
+  between ReadFile and ReadDirectory is the opcode used.\r
+\r
+  @param  This                   The MTFTP protocol session\r
+  @param  Token                  The user's request wrap token.\r
+\r
+  @retval EFI_SUCCESS            The ReadDirectory has finished, the directory has\r
+                                 been  downloaded as a file if it is synchronous\r
+                                 operation,  otherwise it has been initated.\r
+  @retval Others                 Some error happened.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4ReadDirectory (\r
+  IN EFI_MTFTP4_PROTOCOL        *This,\r
+  IN EFI_MTFTP4_TOKEN           *Token\r
+  )\r
+{\r
+  return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_DIR);\r
+}\r
+\r
+\r
+/**\r
+  Poll the network stack to accelerate the packet process.\r
+\r
+  @param  This                   The MTFTP protocol instance.\r
+\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.\r
+  @retval EFI_DEVICE_ERROR       The MTFTP session has been destoried.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp4Poll (\r
+  IN EFI_MTFTP4_PROTOCOL    *This\r
+  )\r
+{\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_UDP4_PROTOCOL         *Udp;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = MTFTP4_PROTOCOL_FROM_THIS (This);\r
+\r
+  if (Instance->State == MTFTP4_STATE_UNCONFIGED) {\r
+    return EFI_NOT_STARTED;\r
+  } else if (Instance->State == MTFTP4_STATE_DESTORY) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Udp = Instance->UnicastPort->Udp;\r
+  return Udp->Poll (Udp);\r
+}\r
+\r
+EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate = {\r
+  EfiMtftp4GetModeData,\r
+  EfiMtftp4Configure,\r
+  EfiMtftp4GetInfo,\r
+  EfiMtftp4ParseOptions,\r
+  EfiMtftp4ReadFile,\r
+  EfiMtftp4WriteFile,\r
+  EfiMtftp4ReadDirectory,\r
+  EfiMtftp4Poll\r
+};\r
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h
new file mode 100644 (file)
index 0000000..24afe6b
--- /dev/null
@@ -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>\r
+\r
+#include <Protocol/Udp4.h>\r
+#include <Protocol/Mtftp4.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#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 (file)
index 0000000..b85784a
--- /dev/null
@@ -0,0 +1,542 @@
+/** @file\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Mtftp4Option.c\r
+\r
+Abstract:\r
+  routines to process MTFTP4 options\r
+\r
+\r
+**/\r
+\r
+#include "Mtftp4Impl.h"\r
+\r
+UINT8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = {\r
+  "blksize",\r
+  "timeout",\r
+  "tsize",\r
+  "multicast"\r
+};\r
+\r
+\r
+/**\r
+  Go through the packet to fill the Options array with the start\r
+  addresses of each MTFTP option name/value pair.\r
+\r
+  @param  Packet                 The packet to check\r
+  @param  PacketLen              The packet's length\r
+  @param  Count                  The size of the Options on input. The actual\r
+                                 options on output\r
+  @param  Options                The option array to fill in\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is mal-formated\r
+  @retval EFI_BUFFER_TOO_SMALL   The Options array is too small\r
+  @retval EFI_SUCCESS            The packet has been parsed into the Options array.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4FillOptions (\r
+  IN  EFI_MTFTP4_PACKET     *Packet,\r
+  IN  UINT32                PacketLen,\r
+  IN  OUT UINT32            *Count,\r
+  OUT EFI_MTFTP4_OPTION     *Options          OPTIONAL\r
+  )\r
+{\r
+  UINT8                     *Cur;\r
+  UINT8                     *Last;\r
+  UINT8                     Num;\r
+  UINT8                     *Name;\r
+  UINT8                     *Value;\r
+\r
+  Num   = 0;\r
+  Cur   = (UINT8 *) Packet + MTFTP4_OPCODE_LEN;\r
+  Last  = (UINT8 *) Packet + PacketLen - 1;\r
+\r
+  //\r
+  // process option name and value pairs. The last byte is always zero\r
+  //\r
+  while (Cur < Last) {\r
+    Name = Cur;\r
+\r
+    while (*Cur != 0) {\r
+      Cur++;\r
+    }\r
+\r
+    if (Cur == Last) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Value = ++Cur;\r
+\r
+    while (*Cur != 0) {\r
+      Cur++;\r
+    }\r
+\r
+    Num++;\r
+\r
+    if ((Options != NULL) && (Num <= *Count)) {\r
+      Options[Num - 1].OptionStr  = Name;\r
+      Options[Num - 1].ValueStr   = Value;\r
+    }\r
+\r
+    Cur++;\r
+  }\r
+\r
+  if ((*Count < Num) || (Options == NULL)) {\r
+    *Count = Num;\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  *Count = Num;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Allocate and fill in a array of Mtftp options from the Packet. It\r
+  first calls Mtftp4FillOption to get the option number, then allocate\r
+  the array, at last, call Mtftp4FillOption again to save the options.\r
+\r
+  @param  Packet                 The packet to parse\r
+  @param  PacketLen              The length of the packet\r
+  @param  OptionCount            The number of options in the packet\r
+  @param  OptionList             The point to get the option array.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parametera are invalid or packet isn't a\r
+                                 well-formated OACK packet.\r
+  @retval EFI_SUCCESS            The option array is build\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the array\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4ExtractOptions (\r
+  IN  EFI_MTFTP4_PACKET     *Packet,\r
+  IN  UINT32                PacketLen,\r
+  IN  OUT UINT32            *OptionCount,\r
+  OUT EFI_MTFTP4_OPTION     **OptionList        OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  *OptionCount = 0;\r
+\r
+  if (OptionList != NULL) {\r
+    *OptionList = NULL;\r
+  }\r
+\r
+  if (NTOHS (Packet->OpCode) != EFI_MTFTP4_OPCODE_OACK) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (PacketLen == MTFTP4_OPCODE_LEN) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // The last byte must be zero to terminate the options\r
+  //\r
+  if (*((UINT8 *) Packet + PacketLen - 1) != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Get the number of options\r
+  //\r
+  Status = Mtftp4FillOptions (Packet, PacketLen, OptionCount, NULL);\r
+\r
+  if ((Status == EFI_SUCCESS) || (Status != EFI_BUFFER_TOO_SMALL)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Allocate memory for the options, then call Mtftp4FillOptions to\r
+  // fill it if caller want that.\r
+  //\r
+  if (OptionList == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  *OptionList = NetAllocatePool (*OptionCount * sizeof (EFI_MTFTP4_OPTION));\r
+\r
+  if (*OptionList == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Mtftp4FillOptions (Packet, PacketLen, OptionCount, *OptionList);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Check whether two ascii strings are equel, ignore the case.\r
+\r
+  @param  Str1                   The first ascii string\r
+  @param  Str2                   The second ascii string\r
+\r
+  @retval TRUE                   Two strings are equal when case is ignored.\r
+  @retval FALSE                  Two string are not equal.\r
+\r
+**/\r
+BOOLEAN\r
+NetStringEqualNoCase (\r
+  IN  UINT8                 *Str1,\r
+  IN  UINT8                 *Str2\r
+  )\r
+{\r
+  UINT8                     Ch1;\r
+  UINT8                     Ch2;\r
+\r
+  ASSERT ((Str1 != NULL) && (Str2 != NULL));\r
+\r
+  for (; (*Str1 != '\0') && (*Str2 != '\0'); Str1++, Str2++) {\r
+    Ch1 = *Str1;\r
+    Ch2 = *Str2;\r
+\r
+    //\r
+    // Convert them to lower case then compare two\r
+    //\r
+    if (('A' <= Ch1) && (Ch1 <= 'Z')) {\r
+      Ch1 += 'a' - 'A';\r
+    }\r
+\r
+    if (('A' <= Ch2) && (Ch2 <= 'Z')) {\r
+      Ch2 += 'a' - 'A';\r
+    }\r
+\r
+    if (Ch1 != Ch2) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  return (BOOLEAN) (*Str1 == *Str2);\r
+}\r
+\r
+\r
+/**\r
+  Convert a string to a UINT32 number.\r
+\r
+  @param  Str                    The string to convert from\r
+\r
+  @return The number get from the string\r
+\r
+**/\r
+UINT32\r
+NetStringToU32 (\r
+  IN  UINT8                 *Str\r
+  )\r
+{\r
+  UINT32                    Num;\r
+\r
+  ASSERT (Str != NULL);\r
+\r
+  Num = 0;\r
+\r
+  for (; NET_IS_DIGIT (*Str); Str++) {\r
+    Num = Num * 10 + (*Str - '0');\r
+  }\r
+\r
+  return Num;\r
+}\r
+\r
+\r
+/**\r
+  Convert a string of the format "192.168.0.1" to an IP address.\r
+\r
+  @param  Str                    The string representation of IP\r
+  @param  Ip                     The varible to get IP.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The IP string is invalid.\r
+  @retval EFI_SUCCESS            The IP is parsed into the Ip\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NetStringToIp (\r
+  IN  UINT8                 *Str,\r
+  OUT IP4_ADDR              *Ip\r
+  )\r
+{\r
+  UINT32                    Byte;\r
+  UINT32                    Addr;\r
+  UINTN                     Index;\r
+\r
+  *Ip  = 0;\r
+  Addr = 0;\r
+\r
+  for (Index = 0; Index < 4; Index++) {\r
+    if (!NET_IS_DIGIT (*Str)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Byte = NetStringToU32 (Str);\r
+\r
+    if (Byte > 255) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Addr = (Addr << 8) | Byte;\r
+\r
+    //\r
+    // Skip all the digitals and check whether the sepeator is the dot\r
+    //\r
+    while (NET_IS_DIGIT (*Str)) {\r
+      Str++;\r
+    }\r
+\r
+    if ((Index < 3) && (*Str != '.')) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Str++;\r
+  }\r
+\r
+  *Ip = Addr;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the MTFTP multicast option.\r
+\r
+  @param  Value                  The Mtftp multicast value string\r
+  @param  Option                 The option to save the info into.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The multicast value string is invalid.\r
+  @retval EFI_SUCCESS            The multicast value is parsed into the Option\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4ExtractMcast (\r
+  IN UINT8                  *Value,\r
+  IN MTFTP4_OPTION          *Option\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  UINT32                    Num;\r
+\r
+  //\r
+  // The multicast option is formated like "204.0.0.1,1857,1"\r
+  // The server can also omit the ip and port, use ",,1"\r
+  //\r
+  if (*Value == ',') {\r
+    Option->McastIp   = 0;\r
+  } else {\r
+    Status = NetStringToIp (Value, &Option->McastIp);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    while (*Value && (*Value != ',')) {\r
+      Value++;\r
+    }\r
+  }\r
+\r
+  if (*Value != ',') {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Value++;\r
+\r
+  //\r
+  // Convert the port setting. the server can send us a port number or\r
+  // empty string. such as the port in ",,1"\r
+  //\r
+  if (*Value == ',') {\r
+    Option->McastPort = 0;\r
+  } else {\r
+    Num = NetStringToU32 (Value);\r
+\r
+    if (Num > 65535) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Option->McastPort = (UINT16)Num;\r
+\r
+    while (NET_IS_DIGIT (*Value)) {\r
+      Value++;\r
+    }\r
+  }\r
+\r
+  if (*Value != ',') {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Value++;\r
+\r
+  //\r
+  // Check the master/slave setting, 1 for master, 0 for slave.\r
+  //\r
+  Num = NetStringToU32 (Value);\r
+\r
+  if ((Num != 0) && (Num != 1)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Option->Master = (BOOLEAN)(Num == 1);\r
+\r
+  while (NET_IS_DIGIT (*Value)) {\r
+    Value++;\r
+  }\r
+\r
+  if (*Value != '\0') {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the option in Options array to MTFTP4_OPTION which program\r
+  can access directly.\r
+\r
+  @param  Options                The option array, which contains addresses of each\r
+                                  option's name/value string.\r
+  @param  Count                  The number of options in the Options\r
+  @param  Request                Whether this is a request or OACK. The format of\r
+                                 multicast is different according to this setting.\r
+  @param  MtftpOption            The MTFTP4_OPTION for easy access.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The option is mal-formated\r
+  @retval EFI_UNSUPPORTED        Some option isn't supported\r
+  @retval EFI_SUCCESS            The option are OK and has been parsed.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4ParseOption (\r
+  IN  EFI_MTFTP4_OPTION     *Options,\r
+  IN  UINT32                Count,\r
+  IN  BOOLEAN               Request,\r
+  OUT MTFTP4_OPTION         *MtftpOption\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  UINT32                    Index;\r
+  UINT32                    Value;\r
+  EFI_MTFTP4_OPTION         *This;\r
+\r
+  MtftpOption->Exist = 0;\r
+\r
+  for (Index = 0; Index < Count; Index++) {\r
+    This = Options + Index;\r
+\r
+    if ((This->OptionStr == NULL) || (This->ValueStr == NULL)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (NetStringEqualNoCase (This->OptionStr, "blksize")) {\r
+      //\r
+      // block size option, valid value is between [8, 65464]\r
+      //\r
+      Value = NetStringToU32 (This->ValueStr);\r
+\r
+      if ((Value < 8) || (Value > 65464)) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      MtftpOption->BlkSize = (UINT16) Value;\r
+      MtftpOption->Exist |= MTFTP4_BLKSIZE_EXIST;\r
+\r
+    } else if (NetStringEqualNoCase (This->OptionStr, "timeout")) {\r
+      //\r
+      // timeout option, valid value is between [1, 255]\r
+      //\r
+      Value = NetStringToU32 (This->ValueStr);\r
+\r
+      if ((Value < 1) || (Value > 255)) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      MtftpOption->Timeout = (UINT8) Value;\r
+\r
+    } else if (NetStringEqualNoCase (This->OptionStr, "tsize")) {\r
+      //\r
+      // tsize option, the biggest transfer supported is 4GB with block size option\r
+      //\r
+      MtftpOption->Tsize = NetStringToU32 (This->ValueStr);\r
+      MtftpOption->Exist |= MTFTP4_TSIZE_EXIST;\r
+\r
+    } else if (NetStringEqualNoCase (This->OptionStr, "multicast")) {\r
+      //\r
+      // Multicast option, if it is a request, the value must be a zero\r
+      // length string, otherwise, it is formated like "204.0.0.1,1857,1\0"\r
+      //\r
+      if (Request) {\r
+        if (*(This->ValueStr) != '\0') {\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+\r
+      } else {\r
+        Status = Mtftp4ExtractMcast (This->ValueStr, MtftpOption);\r
+\r
+        if (EFI_ERROR (Status)) {\r
+          return Status;\r
+        }\r
+      }\r
+\r
+      MtftpOption->Exist |= MTFTP4_MCAST_EXIST;\r
+\r
+    } else if (Request) {\r
+      //\r
+      // Ignore the unsupported option if it is a reply, and return\r
+      // EFI_UNSUPPORTED if it's a request according to the UEFI spec.\r
+      //\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the options in the OACK packet to MTFTP4_OPTION which program\r
+  can access directly.\r
+\r
+  @param  Packet                 The OACK packet to parse\r
+  @param  PacketLen              The length of the packet\r
+  @param  MtftpOption            The MTFTP_OPTION for easy access.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet option is mal-formated\r
+  @retval EFI_UNSUPPORTED        Some option isn't supported\r
+  @retval EFI_SUCCESS            The option are OK and has been parsed.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4ParseOptionOack (\r
+  IN  EFI_MTFTP4_PACKET     *Packet,\r
+  IN  UINT32                PacketLen,\r
+  OUT MTFTP4_OPTION         *MtftpOption\r
+  )\r
+{\r
+  EFI_MTFTP4_OPTION *OptionList;\r
+  EFI_STATUS        Status;\r
+  UINT32            Count;\r
+\r
+  MtftpOption->Exist = 0;\r
+\r
+  Status = Mtftp4ExtractOptions (Packet, PacketLen, &Count, &OptionList);\r
+\r
+  if (EFI_ERROR (Status) || (Count == 0)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Mtftp4ParseOption (OptionList, Count, FALSE, MtftpOption);\r
+\r
+  NetFreePool (OptionList);\r
+  return Status;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h
new file mode 100644 (file)
index 0000000..a136304
--- /dev/null
@@ -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 (file)
index 0000000..665114c
--- /dev/null
@@ -0,0 +1,735 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Mtftp4Rrq.c\r
+\r
+Abstract:\r
+\r
+  Routines to process Rrq (download)\r
+\r
+\r
+**/\r
+\r
+#include "Mtftp4Impl.h"\r
+\r
+VOID\r
+Mtftp4RrqInput (\r
+  IN NET_BUF                *UdpPacket,\r
+  IN UDP_POINTS             *Points,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+\r
+/**\r
+  Start the MTFTP session to download. It will first initialize some\r
+  of the internal states then build and send a RRQ reqeuest packet, at\r
+  last, it will start receive for the downloading.\r
+\r
+  @param  Instance              The Mtftp session\r
+  @param  Operation             The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ\r
+                                or EFI_MTFTP4_OPCODE_DIR.\r
+\r
+  @retval EFI_SUCCESS           The mtftp download session is started.\r
+  @retval Others                Failed to start downloading.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4RrqStart (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN UINT16                 Operation\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // The valid block number range are [1, 0xffff]. For example:\r
+  // the client sends an RRQ request to the server, the server\r
+  // transfers the DATA1 block. If option negoitation is ongoing,\r
+  // the server will send back an OACK, then client will send ACK0.\r
+  //\r
+  Status = Mtftp4InitBlockRange (&Instance->Blocks, 1, 0xffff);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Mtftp4SendRequest (Instance);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);\r
+}\r
+\r
+\r
+/**\r
+  Build and send a ACK packet for the download session.\r
+\r
+  @param  Instance              The Mtftp session\r
+  @param  BlkNo                 The BlkNo to ack.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the packet\r
+  @retval EFI_SUCCESS           The ACK has been sent\r
+  @retval Others                Failed to send the ACK.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4RrqSendAck (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN UINT16                 BlkNo\r
+  )\r
+{\r
+  EFI_MTFTP4_PACKET         *Ack;\r
+  NET_BUF                   *Packet;\r
+\r
+  Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));\r
+\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ack = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (\r
+                                Packet,\r
+                                sizeof (EFI_MTFTP4_ACK_HEADER),\r
+                                FALSE\r
+                                );\r
+\r
+  Ack->Ack.OpCode   = HTONS (EFI_MTFTP4_OPCODE_ACK);\r
+  Ack->Ack.Block[0] = HTONS (BlkNo);\r
+\r
+  return Mtftp4SendPacket (Instance, Packet);\r
+}\r
+\r
+\r
+/**\r
+  Deliver the received data block to the user, which can be saved\r
+  in the user provide buffer or through the CheckPacket callback.\r
+\r
+  @param  Instance              The Mtftp session\r
+  @param  Packet                The received data packet\r
+  @param  Len                   The packet length\r
+\r
+  @retval EFI_SUCCESS           The data is saved successfully\r
+  @retval EFI_ABORTED           The user tells to abort by return an error  through\r
+                                CheckPacket\r
+  @retval EFI_BUFFER_TOO_SMALL  The user's buffer is too small and buffer length is\r
+                                 updated to the actual buffer size needed.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4RrqSaveBlock (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN EFI_MTFTP4_PACKET      *Packet,\r
+  IN UINT32                 Len\r
+  )\r
+{\r
+  EFI_MTFTP4_TOKEN          *Token;\r
+  EFI_STATUS                Status;\r
+  UINT16                    Block;\r
+  UINT64                    Start;\r
+  UINT32                    DataLen;\r
+\r
+  Token   = Instance->Token;\r
+  Block   = NTOHS (Packet->Data.Block);\r
+  DataLen = Len - MTFTP4_DATA_HEAD_LEN;\r
+\r
+  //\r
+  // This is the last block, save the block no\r
+  //\r
+  if (DataLen < Instance->BlkSize) {\r
+    Instance->LastBlock = Block;\r
+    Mtftp4SetLastBlockNum (&Instance->Blocks, Block);\r
+  }\r
+\r
+  //\r
+  // Remove this block number from the file hole. If Mtftp4RemoveBlockNum\r
+  // returns EFI_NOT_FOUND, the block has been saved, don't save it again.\r
+  //\r
+  Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block);\r
+\r
+  if (Status == EFI_NOT_FOUND) {\r
+    return EFI_SUCCESS;\r
+  } else if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Token->CheckPacket != NULL) {\r
+    Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16) Len, Packet);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      Mtftp4SendError (\r
+        Instance,\r
+        EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,\r
+        "User aborted download"\r
+        );\r
+\r
+      return EFI_ABORTED;\r
+    }\r
+  }\r
+\r
+  if (Token->Buffer != NULL) {\r
+    Start = MultU64x32 (Block - 1, Instance->BlkSize);\r
+\r
+    if (Start + DataLen <= Token->BufferSize) {\r
+      NetCopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);\r
+\r
+      //\r
+      // Update the file size when received the last block\r
+      //\r
+      if (Instance->LastBlock == Block) {\r
+        Token->BufferSize = Start + DataLen;\r
+      }\r
+\r
+    } else if (Instance->LastBlock != 0) {\r
+      //\r
+      // Don't save the data if the buffer is too small, return\r
+      // EFI_BUFFER_TOO_SMALL if received the last packet. This\r
+      // will give a accurate file length.\r
+      //\r
+      Token->BufferSize = Start + DataLen;\r
+\r
+      Mtftp4SendError (\r
+        Instance,\r
+        EFI_MTFTP4_ERRORCODE_DISK_FULL,\r
+        "User provided memory block is too small"\r
+        );\r
+\r
+      return EFI_BUFFER_TOO_SMALL;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Function to process the received data packets. It will save the block\r
+  then send back an ACK if it is active.\r
+\r
+  @param  Instance              The downloading MTFTP session\r
+  @param  Packet                The packet received\r
+  @param  Len                   The length of the packet\r
+  @param  Multicast             Whether this packet is multicast or unicast\r
+  @param  Completed             Return whether the download has completed\r
+\r
+  @retval EFI_SUCCESS           The data packet is successfully processed\r
+  @retval EFI_ABORTED           The download is aborted by the user\r
+  @retval EFI_BUFFER_TOO_SMALL  The user provided buffer is too small\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4RrqHandleData (\r
+  IN  MTFTP4_PROTOCOL       *Instance,\r
+  IN  EFI_MTFTP4_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  IN  BOOLEAN               Multicast,\r
+  OUT BOOLEAN               *Completed\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  UINT16                    BlockNum;\r
+  INTN                      Expected;\r
+\r
+  *Completed  = FALSE;\r
+  BlockNum    = NTOHS (Packet->Data.Block);\r
+  Expected    = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
+\r
+  ASSERT (Expected >= 0);\r
+\r
+  //\r
+  // If we are active and received an unexpected packet, retransmit\r
+  // the last ACK then restart receiving. If we are passive, save\r
+  // the block.\r
+  //\r
+  if (Instance->Master && (Expected != BlockNum)) {\r
+    Mtftp4Retransmit (Instance);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Reset the passive client's timer whenever it received a\r
+  // valid data packet.\r
+  //\r
+  if (!Instance->Master) {\r
+    Mtftp4SetTimeout (Instance);\r
+  }\r
+\r
+  //\r
+  // Check whether we have received all the blocks. Send the ACK if we\r
+  // are active (unicast client or master client for multicast download).\r
+  // If we have received all the blocks, send an ACK even if we are passive\r
+  // to tell the server that we are done.\r
+  //\r
+  Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
+\r
+  if (Instance->Master || (Expected < 0)) {\r
+    if (Expected < 0) {\r
+      //\r
+      // If we are passive client, then the just received Block maybe\r
+      // isn't the last block. We need to send an ACK to the last block\r
+      // to inform the server that we are done. If we are active client,\r
+      // the Block == Instance->LastBlock.\r
+      //\r
+      BlockNum   = Instance->LastBlock;\r
+      *Completed = TRUE;\r
+\r
+    } else {\r
+      BlockNum = (UINT16) (Expected - 1);\r
+    }\r
+\r
+    Mtftp4RrqSendAck (Instance, BlockNum);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Validate whether the options received in the server's OACK packet is valid.\r
+  The options are valid only if:\r
+  1. The server doesn't include options not requested by us\r
+  2. The server can only use smaller blksize than that is requested\r
+  3. The server can only use the same timeout as requested\r
+  4. The server doesn't change its multicast channel.\r
+\r
+  @param  This                  The downloading Mtftp session\r
+  @param  Reply                 The options in the OACK packet\r
+  @param  Request               The requested options\r
+\r
+  @return TRUE if the options in the OACK is OK, otherwise FALSE.\r
+\r
+**/\r
+BOOLEAN\r
+Mtftp4RrqOackValid (\r
+  IN MTFTP4_PROTOCOL        *This,\r
+  IN MTFTP4_OPTION          *Reply,\r
+  IN MTFTP4_OPTION          *Request\r
+  )\r
+{\r
+\r
+  //\r
+  // It is invalid for server to return options we don't request\r
+  //\r
+  if ((Reply->Exist &~Request->Exist) != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Server can only specify a smaller block size to be used and\r
+  // return the timeout matches that requested.\r
+  //\r
+  if (((Reply->Exist & MTFTP4_BLKSIZE_EXIST) && (Reply->BlkSize > Request->BlkSize)) ||\r
+      ((Reply->Exist & MTFTP4_TIMEOUT_EXIST) && (Reply->Timeout != Request->Timeout))) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // The server can send ",,master" to client to change its master\r
+  // setting. But if it use the specific multicast channel, it can't\r
+  // change the setting.\r
+  //\r
+  if ((Reply->Exist & MTFTP4_MCAST_EXIST) && (This->McastIp != 0)) {\r
+    if ((Reply->McastIp != 0) && (Reply->McastIp != This->McastIp)) {\r
+      return FALSE;\r
+    }\r
+\r
+    if ((Reply->McastPort != 0) && (Reply->McastPort != This->McastPort)) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Configure a UDP IO port to receive the multicast.\r
+\r
+  @param  McastIo               The UDP IO port to configure\r
+  @param  Context               The opaque parameter to the function which is the\r
+                                MTFTP session.\r
+\r
+  @retval EFI_SUCCESS           The udp child is successfully configured.\r
+  @retval Others                Failed to configure the UDP child.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4RrqConfigMcastPort (\r
+  IN UDP_IO_PORT            *McastIo,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_MTFTP4_CONFIG_DATA    *Config;\r
+  EFI_UDP4_CONFIG_DATA      UdpConfig;\r
+  EFI_IPv4_ADDRESS          Group;\r
+  EFI_STATUS                Status;\r
+  IP4_ADDR                  Ip;\r
+\r
+  Instance                     = (MTFTP4_PROTOCOL *) Context;\r
+  Config                       = &Instance->Config;\r
+\r
+  UdpConfig.AcceptBroadcast    = FALSE;\r
+  UdpConfig.AcceptPromiscuous  = FALSE;\r
+  UdpConfig.AcceptAnyPort      = FALSE;\r
+  UdpConfig.AllowDuplicatePort = FALSE;\r
+  UdpConfig.TypeOfService      = 0;\r
+  UdpConfig.TimeToLive         = 64;\r
+  UdpConfig.DoNotFragment      = FALSE;\r
+  UdpConfig.ReceiveTimeout     = 0;\r
+  UdpConfig.TransmitTimeout    = 0;\r
+  UdpConfig.UseDefaultAddress  = Config->UseDefaultSetting;\r
+  UdpConfig.StationAddress     = Config->StationIp;\r
+  UdpConfig.SubnetMask         = Config->SubnetMask;\r
+  UdpConfig.StationPort        = Instance->McastPort;\r
+  UdpConfig.RemotePort         = 0;\r
+\r
+  Ip = HTONL (Instance->ServerIp);\r
+  NetCopyMem (&UdpConfig.RemoteAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Status = McastIo->Udp->Configure (McastIo->Udp, &UdpConfig);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // join the multicast group\r
+  //\r
+  Ip = HTONL (Instance->McastIp);\r
+  NetCopyMem (&Group, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  return McastIo->Udp->Groups (McastIo->Udp, TRUE, &Group);\r
+}\r
+\r
+\r
+/**\r
+  Function to process the OACK. It will first validate the OACK\r
+  packet, then update the various negotiated parameters.\r
+\r
+  @param  Instance              The download MTFTP session\r
+  @param  Packet                The packet received\r
+  @param  Len                   The packet length\r
+  @param  Multicast             Whether this packet is received as a multicast\r
+  @param  Completed             Returns whether the download has completed. NOT\r
+                                used  by this function.\r
+\r
+  @retval EFI_DEVICE_ERROR      Failed to create/start a multicast UDP child\r
+  @retval EFI_TFTP_ERROR        Some error happened during the process\r
+  @retval EFI_SUCCESS           The OACK is successfully processed.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4RrqHandleOack (\r
+  IN  MTFTP4_PROTOCOL       *Instance,\r
+  IN  EFI_MTFTP4_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  IN  BOOLEAN               Multicast,\r
+  OUT BOOLEAN               *Completed\r
+  )\r
+{\r
+  MTFTP4_OPTION             Reply;\r
+  EFI_STATUS                Status;\r
+  INTN                      Expected;\r
+\r
+  *Completed = FALSE;\r
+\r
+  //\r
+  // If already started the master download, don't change the\r
+  // setting. Master download always succeeds.\r
+  //\r
+  Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
+  ASSERT (Expected != -1);\r
+\r
+  if (Instance->Master && (Expected != 1)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Parse and validate the options from server\r
+  //\r
+  NetZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
+\r
+  Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);\r
+\r
+  if (EFI_ERROR (Status) ||\r
+      !Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {\r
+    //\r
+    // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.\r
+    //\r
+    if (Status != EFI_OUT_OF_RESOURCES) {\r
+      Mtftp4SendError (\r
+        Instance,\r
+        EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,\r
+        "Mal-formated OACK packet"\r
+        );\r
+    }\r
+\r
+    return EFI_TFTP_ERROR;\r
+  }\r
+\r
+  if (Reply.Exist & MTFTP4_MCAST_EXIST) {\r
+\r
+    //\r
+    // Save the multicast info. Always update the Master, only update the\r
+    // multicast IP address, block size, timeoute at the first time. If IP\r
+    // address is updated, create a UDP child to receive the multicast.\r
+    //\r
+    Instance->Master = Reply.Master;\r
+\r
+    if (Instance->McastIp == 0) {\r
+      if ((Reply.McastIp == 0) || (Reply.McastPort == 0)) {\r
+        Mtftp4SendError (\r
+          Instance,\r
+          EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,\r
+          "Illegal multicast setting"\r
+          );\r
+\r
+        return EFI_TFTP_ERROR;\r
+      }\r
+\r
+      //\r
+      // Create a UDP child then start receive the multicast from it.\r
+      //\r
+      Instance->McastIp      = Reply.McastIp;\r
+      Instance->McastPort    = Reply.McastPort;\r
+      Instance->McastUdpPort = UdpIoCreatePort (\r
+                                 Instance->Service->Controller,\r
+                                 Instance->Service->Image,\r
+                                 Mtftp4RrqConfigMcastPort,\r
+                                 Instance\r
+                                 );\r
+\r
+      if (Instance->McastUdpPort == NULL) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        Mtftp4SendError (\r
+          Instance,\r
+          EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION,\r
+          "Failed to create socket to receive multicast packet"\r
+          );\r
+\r
+        return Status;\r
+      }\r
+\r
+      //\r
+      // Update the parameters used.\r
+      //\r
+      if (Reply.BlkSize != 0) {\r
+        Instance->BlkSize = Reply.BlkSize;\r
+      }\r
+\r
+      if (Reply.Timeout != 0) {\r
+        Instance->Timeout = Reply.Timeout;\r
+      }\r
+    }\r
+\r
+  } else {\r
+    Instance->Master = TRUE;\r
+\r
+    if (Reply.BlkSize != 0) {\r
+      Instance->BlkSize = Reply.BlkSize;\r
+    }\r
+\r
+    if (Reply.Timeout != 0) {\r
+      Instance->Timeout = Reply.Timeout;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Send an ACK to (Expected - 1) which is 0 for unicast download,\r
+  // or tell the server we want to receive the Expected block.\r
+  //\r
+  return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));\r
+}\r
+\r
+\r
+/**\r
+  The packet process callback for MTFTP download.\r
+\r
+  @param  UdpPacket             The packet received\r
+  @param  Points                The local/remote access point of the packet\r
+  @param  IoStatus              The status of the receiving\r
+  @param  Context               Opaque parameter, which is the MTFTP session\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Mtftp4RrqInput (\r
+  IN NET_BUF                *UdpPacket,\r
+  IN UDP_POINTS             *Points,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_MTFTP4_PACKET         *Packet;\r
+  BOOLEAN                   Completed;\r
+  BOOLEAN                   Multicast;\r
+  EFI_STATUS                Status;\r
+  UINT16                    Opcode;\r
+  UINT32                    Len;\r
+\r
+  Instance  = (MTFTP4_PROTOCOL *) Context;\r
+  NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);\r
+\r
+  Status    = EFI_SUCCESS;\r
+  Packet    = NULL;\r
+  Completed = FALSE;\r
+  Multicast = FALSE;\r
+\r
+  if (EFI_ERROR (IoStatus)) {\r
+    Status = IoStatus;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  ASSERT (UdpPacket != NULL);\r
+\r
+  //\r
+  // Find the port this packet is from to restart receive correctly.\r
+  //\r
+  Multicast = (BOOLEAN) (Points->LocalAddr == Instance->McastIp);\r
+\r
+  if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Client send initial request to server's listening port. Server\r
+  // will select a UDP port to communicate with the client. The server\r
+  // is required to use the same port as RemotePort to multicast the\r
+  // data.\r
+  //\r
+  if (Points->RemotePort != Instance->ConnectedPort) {\r
+    if (Instance->ConnectedPort != 0) {\r
+      goto ON_EXIT;\r
+    } else {\r
+      Instance->ConnectedPort = Points->RemotePort;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
+  //\r
+  Len = UdpPacket->TotalSize;\r
+\r
+  if (UdpPacket->BlockOpNum > 1) {\r
+    Packet = NetAllocatePool (Len);\r
+\r
+    if (Packet == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
+\r
+  } else {\r
+    Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
+  }\r
+\r
+  Opcode = NTOHS (Packet->OpCode);\r
+\r
+  //\r
+  // Call the user's CheckPacket if provided. Abort the transmission\r
+  // if CheckPacket returns an EFI_ERROR code.\r
+  //\r
+  if ((Instance->Token->CheckPacket != NULL) &&\r
+      ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {\r
+\r
+    Status = Instance->Token->CheckPacket (\r
+                                &Instance->Mtftp4,\r
+                                Instance->Token,\r
+                                (UINT16) Len,\r
+                                Packet\r
+                                );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Send an error message to the server to inform it\r
+      //\r
+      if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {\r
+        Mtftp4SendError (\r
+          Instance,\r
+          EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
+          "User aborted the transfer"\r
+          );\r
+      }\r
+\r
+      Status = EFI_ABORTED;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  switch (Opcode) {\r
+  case EFI_MTFTP4_OPCODE_DATA:\r
+    if ((Len > (UINT32) (MTFTP4_DATA_HEAD_LEN + Instance->BlkSize)) ||\r
+        (Len < (UINT32) MTFTP4_DATA_HEAD_LEN)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status = Mtftp4RrqHandleData (Instance, Packet, Len, Multicast, &Completed);\r
+    break;\r
+\r
+  case EFI_MTFTP4_OPCODE_OACK:\r
+    if (Multicast || (Len <= MTFTP4_OPCODE_LEN)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status = Mtftp4RrqHandleOack (Instance, Packet, Len, Multicast, &Completed);\r
+    break;\r
+\r
+  case EFI_MTFTP4_OPCODE_ERROR:\r
+    Status = EFI_TFTP_ERROR;\r
+    break;\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  //\r
+  // Free the resources, then if !EFI_ERROR (Status), restart the\r
+  // receive, otherwise end the session.\r
+  //\r
+  if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {\r
+    NetFreePool (Packet);\r
+  }\r
+\r
+  if (UdpPacket != NULL) {\r
+    NetbufFree (UdpPacket);\r
+  }\r
+\r
+  if (!EFI_ERROR (Status) && !Completed) {\r
+    if (Multicast) {\r
+      Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);\r
+    } else {\r
+      Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);\r
+    }\r
+  }\r
+\r
+  if (EFI_ERROR (Status) || Completed) {\r
+    Mtftp4CleanOperation (Instance, Status);\r
+  }\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c
new file mode 100644 (file)
index 0000000..af61c0b
--- /dev/null
@@ -0,0 +1,591 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Mtftp4Support.c\r
+\r
+Abstract:\r
+\r
+  Support routines for Mtftp\r
+\r
+\r
+**/\r
+\r
+#include "Mtftp4Impl.h"\r
+\r
+\r
+/**\r
+  Allocate a MTFTP4 block range, then init it to the\r
+  range of [Start, End]\r
+\r
+  @param  Start                 The start block number\r
+  @param  End                   The last block number in the range\r
+\r
+  @return NULL if failed to allocate memory, otherwise the created block range.\r
+\r
+**/\r
+STATIC\r
+MTFTP4_BLOCK_RANGE *\r
+Mtftp4AllocateRange (\r
+  IN UINT16                 Start,\r
+  IN UINT16                 End\r
+  )\r
+{\r
+  MTFTP4_BLOCK_RANGE        *Range;\r
+\r
+  Range = NetAllocatePool (sizeof (MTFTP4_BLOCK_RANGE));\r
+\r
+  if (Range == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NetListInit (&Range->Link);\r
+  Range->Start  = Start;\r
+  Range->End    = End;\r
+\r
+  return Range;\r
+}\r
+\r
+\r
+/**\r
+  Initialize the block range for either RRQ or WRQ. RRQ and WRQ have\r
+  different requirements for Start and End. For example, during start\r
+  up, WRQ initializes its whole valid block range to [0, 0xffff]. This\r
+  is bacause the server will send us a ACK0 to inform us to start the\r
+  upload. When the client received ACK0, it will remove 0 from the range,\r
+  get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
+  without option negotiation, the server will directly send us the BLOCK1\r
+  in response to the client's RRQ. When received BLOCK1, the client will\r
+  remove it from the block range and send an ACK. It also works if there\r
+  is option negotiation.\r
+\r
+  @param  Head                  The block range head to initialize\r
+  @param  Start                 The Start block number.\r
+  @param  End                   The last block number.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for initial block range\r
+  @retval EFI_SUCCESS           The initial block range is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4InitBlockRange (\r
+  IN NET_LIST_ENTRY         *Head,\r
+  IN UINT16                 Start,\r
+  IN UINT16                 End\r
+  )\r
+{\r
+  MTFTP4_BLOCK_RANGE        *Range;\r
+\r
+  Range = Mtftp4AllocateRange (Start, End);\r
+\r
+  if (Range == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  NetListInsertTail (Head, &Range->Link);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Get the first valid block number on the range list.\r
+\r
+  @param  Head                  The block range head\r
+\r
+  @return -1: if the block range is empty. Otherwise the first valid block number.\r
+\r
+**/\r
+INTN\r
+Mtftp4GetNextBlockNum (\r
+  IN NET_LIST_ENTRY         *Head\r
+  )\r
+{\r
+  MTFTP4_BLOCK_RANGE  *Range;\r
+\r
+  if (NetListIsEmpty (Head)) {\r
+    return -1;\r
+  }\r
+\r
+  Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);\r
+  return Range->Start;\r
+}\r
+\r
+\r
+/**\r
+  Set the last block number of the block range list. It will\r
+  remove all the blocks after the Last. MTFTP initialize the\r
+  block range to the maximum possible range, such as [0, 0xffff]\r
+  for WRQ. When it gets the last block number, it will call\r
+  this function to set the last block number.\r
+\r
+  @param  Head                  The block range list\r
+  @param  Last                  The last block number\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Mtftp4SetLastBlockNum (\r
+  IN NET_LIST_ENTRY         *Head,\r
+  IN UINT16                 Last\r
+  )\r
+{\r
+  MTFTP4_BLOCK_RANGE        *Range;\r
+\r
+  //\r
+  // Iterate from the tail to head to remove the block number\r
+  // after the last.\r
+  //\r
+  while (!NetListIsEmpty (Head)) {\r
+    Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);\r
+\r
+    if (Range->Start > Last) {\r
+      NetListRemoveEntry (&Range->Link);\r
+      NetFreePool (Range);\r
+      continue;\r
+    }\r
+\r
+    if (Range->End > Last) {\r
+      Range->End = Last;\r
+    }\r
+\r
+    return ;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Remove the block number from the block range list.\r
+\r
+  @param  Head                  The block range list to remove from\r
+  @param  Num                   The block number to remove\r
+\r
+  @retval EFI_NOT_FOUND         The block number isn't in the block range list\r
+  @retval EFI_SUCCESS           The block number has been removed from the list\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4RemoveBlockNum (\r
+  IN NET_LIST_ENTRY         *Head,\r
+  IN UINT16                 Num\r
+  )\r
+{\r
+  MTFTP4_BLOCK_RANGE        *Range;\r
+  MTFTP4_BLOCK_RANGE        *NewRange;\r
+  NET_LIST_ENTRY            *Entry;\r
+\r
+  NET_LIST_FOR_EACH (Entry, Head) {\r
+\r
+    //\r
+    // Each block represents a hole [Start, End] in the file,\r
+    // skip to the first range with End >= Num\r
+    //\r
+    Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);\r
+\r
+    if (Range->End < Num) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // There are three different cases for Start\r
+    // 1. (Start > Num) && (End >= Num):\r
+    //    because all the holes before this one has the condition of\r
+    //    End < Num, so this block number has been removed.\r
+    //\r
+    // 2. (Start == Num) && (End >= Num):\r
+    //    Need to increase the Start by one, and if End == Num, this\r
+    //    hole has been removed completely, remove it.\r
+    //\r
+    // 3. (Start < Num) && (End >= Num):\r
+    //    if End == Num, only need to decrease the End by one because\r
+    //    we have (Start < Num) && (Num == End), so (Start <= End - 1).\r
+    //    if (End > Num), the hold is splited into two holes, with\r
+    //    [Start, Num - 1] and [Num + 1, End].\r
+    //\r
+    if (Range->Start > Num) {\r
+      return EFI_NOT_FOUND;\r
+\r
+    } else if (Range->Start == Num) {\r
+      Range->Start++;\r
+\r
+      if (Range->Start > Range->End) {\r
+        NetListRemoveEntry (&Range->Link);\r
+        NetFreePool (Range);\r
+      }\r
+\r
+      return EFI_SUCCESS;\r
+\r
+    } else {\r
+      if (Range->End == Num) {\r
+        Range->End--;\r
+      } else {\r
+        NewRange = Mtftp4AllocateRange (Num + 1, (UINT16) Range->End);\r
+\r
+        if (NewRange == NULL) {\r
+          return EFI_OUT_OF_RESOURCES;\r
+        }\r
+\r
+        Range->End = Num - 1;\r
+        NetListInsertAfter (&Range->Link, &NewRange->Link);\r
+      }\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+  Build then transmit the request packet for the MTFTP session.\r
+\r
+  @param  Instance              The Mtftp session\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the request\r
+  @retval EFI_SUCCESS           The request is built and sent\r
+  @retval Others                Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4SendRequest (\r
+  IN MTFTP4_PROTOCOL        *Instance\r
+  )\r
+{\r
+  EFI_MTFTP4_PACKET         *Packet;\r
+  EFI_MTFTP4_OPTION         *Options;\r
+  EFI_MTFTP4_TOKEN          *Token;\r
+  NET_BUF                   *Nbuf;\r
+  UINT8                     *Mode;\r
+  UINT8                     *Cur;\r
+  UINT32                    Len;\r
+  UINTN                     Index;\r
+\r
+  Token   = Instance->Token;\r
+  Options = Token->OptionList;\r
+  Mode    = Instance->Token->ModeStr;\r
+\r
+  if (Mode == NULL) {\r
+    Mode = "octet";\r
+  }\r
+\r
+  //\r
+  // Compute the packet length\r
+  //\r
+  Len = (UINT32) (AsciiStrLen (Token->Filename) + AsciiStrLen (Mode) + 4);\r
+\r
+  for (Index = 0; Index < Token->OptionCount; Index++) {\r
+    Len += (UINT32) (AsciiStrLen (Options[Index].OptionStr) +\r
+                     AsciiStrLen (Options[Index].ValueStr) + 2);\r
+  }\r
+\r
+  //\r
+  // Allocate a packet then copy the data over\r
+  //\r
+  if ((Nbuf = NetbufAlloc (Len)) == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet         = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);\r
+  Packet->OpCode = HTONS (Instance->Operation);\r
+  Cur            = Packet->Rrq.Filename;\r
+  Cur            = AsciiStrCpy (Cur, Token->Filename);\r
+  Cur            = AsciiStrCpy (Cur, Mode);\r
+\r
+  for (Index = 0; Index < Token->OptionCount; ++Index) {\r
+    Cur = AsciiStrCpy (Cur, Options[Index].OptionStr);\r
+    Cur = AsciiStrCpy (Cur, Options[Index].ValueStr);\r
+  }\r
+\r
+  return Mtftp4SendPacket (Instance, Nbuf);\r
+}\r
+\r
+\r
+/**\r
+  Build then send an error message\r
+\r
+  @param  Instance              The MTFTP session\r
+  @param  ErrInfo               The error code and message\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the error packet\r
+  @retval EFI_SUCCESS           The error packet is transmitted.\r
+  @retval Others                Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4SendError (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN UINT16                 ErrCode,\r
+  IN UINT8*                 ErrInfo\r
+  )\r
+{\r
+  NET_BUF                   *Packet;\r
+  EFI_MTFTP4_PACKET         *TftpError;\r
+  UINT32                    Len;\r
+\r
+  Len     = (UINT32) (AsciiStrLen (ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));\r
+  Packet  = NetbufAlloc (Len);\r
+\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TftpError         = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);\r
+  TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);\r
+  TftpError->Error.ErrorCode = HTONS (ErrCode);\r
+\r
+  AsciiStrCpy (TftpError->Error.ErrorMessage, ErrInfo);\r
+\r
+  return Mtftp4SendPacket (Instance, Packet);\r
+}\r
+\r
+\r
+/**\r
+  The callback function called when the packet is transmitted.\r
+  It simply frees the packet.\r
+\r
+  @param  Packet                The transmitted (or failed to) packet\r
+  @param  Points                The local and remote UDP access point\r
+  @param  IoStatus              The result of the transmission\r
+  @param  Context               Opaque parameter to the callback\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Mtftp4OnPacketSent (\r
+  NET_BUF                   *Packet,\r
+  UDP_POINTS                *Points,\r
+  EFI_STATUS                IoStatus,\r
+  VOID                      *Context\r
+  )\r
+{\r
+  NetbufFree (Packet);\r
+}\r
+\r
+\r
+/**\r
+  Set the timeout for the instance. User a longer time for\r
+  passive instances.\r
+\r
+  @param  Instance              The Mtftp session to set time out\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Mtftp4SetTimeout (\r
+  IN MTFTP4_PROTOCOL        *Instance\r
+  )\r
+{\r
+  if (Instance->Master) {\r
+    Instance->PacketToLive = Instance->Timeout;\r
+  } else {\r
+    Instance->PacketToLive = Instance->Timeout * 2;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Send the packet for the instance. It will first save a reference to\r
+  the packet for later retransmission. then determine the destination\r
+  port, listen port for requests, and connected port for others. At last,\r
+  send the packet out.\r
+\r
+  @param  Instance              The Mtftp instance\r
+  @param  Packet                The packet to send\r
+\r
+  @retval EFI_SUCCESS           The packet is sent out\r
+  @retval Others                Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4SendPacket (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  UDP_POINTS                UdpPoint;\r
+  EFI_STATUS                Status;\r
+  UINT16                    OpCode;\r
+\r
+  //\r
+  // Save the packet for retransmission\r
+  //\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+  }\r
+\r
+  Instance->LastPacket  = Packet;\r
+\r
+  Instance->CurRetry    = 0;\r
+  Mtftp4SetTimeout (Instance);\r
+\r
+  UdpPoint.LocalAddr    = 0;\r
+  UdpPoint.LocalPort    = 0;\r
+  UdpPoint.RemoteAddr   = Instance->ServerIp;\r
+\r
+  //\r
+  // Send the requests to the listening port, other packets\r
+  // to the connected port\r
+  //\r
+  OpCode = NTOHS (*((UINT16 *) NetbufGetByte (Packet, 0, NULL)));\r
+\r
+  if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
+      (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {\r
+    UdpPoint.RemotePort = Instance->ListeningPort;\r
+  } else {\r
+    UdpPoint.RemotePort = Instance->ConnectedPort;\r
+  }\r
+\r
+  NET_GET_REF (Packet);\r
+\r
+  Status = UdpIoSendDatagram (\r
+             Instance->UnicastPort,\r
+             Packet,\r
+             &UdpPoint,\r
+             Instance->Gateway,\r
+             Mtftp4OnPacketSent,\r
+             Instance\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NET_PUT_REF (Packet);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Retransmit the last packet for the instance\r
+\r
+  @param  Instance              The Mtftp instance\r
+\r
+  @retval EFI_SUCCESS           The last packet is retransmitted.\r
+  @retval Others                Failed to retransmit.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4Retransmit (\r
+  IN MTFTP4_PROTOCOL        *Instance\r
+  )\r
+{\r
+  UDP_POINTS                UdpPoint;\r
+  EFI_STATUS                Status;\r
+  UINT16                    OpCode;\r
+\r
+  ASSERT (Instance->LastPacket != NULL);\r
+\r
+  UdpPoint.LocalAddr  = 0;\r
+  UdpPoint.LocalPort  = 0;\r
+  UdpPoint.RemoteAddr = Instance->ServerIp;\r
+\r
+  //\r
+  // Set the requests to the listening port, other packets to the connected port\r
+  //\r
+  OpCode = NTOHS (*(UINT16 *) NetbufGetByte (Instance->LastPacket, 0, NULL));\r
+\r
+  if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
+      (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {\r
+    UdpPoint.RemotePort = Instance->ListeningPort;\r
+  } else {\r
+    UdpPoint.RemotePort = Instance->ConnectedPort;\r
+  }\r
+\r
+  NET_GET_REF (Instance->LastPacket);\r
+\r
+  Status = UdpIoSendDatagram (\r
+             Instance->UnicastPort,\r
+             Instance->LastPacket,\r
+             &UdpPoint,\r
+             Instance->Gateway,\r
+             Mtftp4OnPacketSent,\r
+             Instance\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NET_PUT_REF (Instance->LastPacket);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The timer ticking function for the Mtftp service instance.\r
+\r
+  @param  Event                 The ticking event\r
+  @param  Context               The Mtftp service instance\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp4OnTimerTick (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  MTFTP4_SERVICE            *MtftpSb;\r
+  NET_LIST_ENTRY            *Entry;\r
+  NET_LIST_ENTRY            *Next;\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_MTFTP4_TOKEN          *Token;\r
+\r
+  MtftpSb = (MTFTP4_SERVICE *) Context;\r
+\r
+  //\r
+  // Iterate through all the children of the Mtftp service instance. Time\r
+  // out the packet. If maximum retries reached, clean the session up.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {\r
+    Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);\r
+\r
+    if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Call the user's time out handler\r
+    //\r
+    Token = Instance->Token;\r
+\r
+    if ((Token->TimeoutCallback != NULL) &&\r
+        EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {\r
+\r
+      Mtftp4SendError (\r
+         Instance,\r
+         EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
+         "User aborted the transfer in time out"\r
+         );\r
+\r
+      Mtftp4CleanOperation (Instance, EFI_ABORTED);\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Retransmit the packet if haven't reach the maxmium retry count,\r
+    // otherwise exit the transfer.\r
+    //\r
+    if (++Instance->CurRetry < Instance->MaxRetry) {\r
+      Mtftp4Retransmit (Instance);\r
+      Mtftp4SetTimeout (Instance);\r
+    } else {\r
+      Mtftp4CleanOperation (Instance, EFI_TIMEOUT);\r
+      continue;\r
+    }\r
+  }\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h
new file mode 100644 (file)
index 0000000..be18686
--- /dev/null
@@ -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 (file)
index 0000000..5ac5dc8
--- /dev/null
@@ -0,0 +1,522 @@
+/** @file\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Mtftp4Wrq.c\r
+\r
+Abstract:\r
+\r
+  Routines to process Wrq (upload)\r
+\r
+\r
+**/\r
+\r
+#include "Mtftp4Impl.h"\r
+\r
+VOID\r
+Mtftp4WrqInput (\r
+  IN NET_BUF                *UdpPacket,\r
+  IN UDP_POINTS             *Points,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+\r
+/**\r
+  Start the MTFTP session for pload. It will first init some states,\r
+  then send the WRQ request packet, and start receiving the  packet.\r
+\r
+  @param  Instance              The MTFTP session\r
+  @param  Operation             Redundant parameter, which is always\r
+                                EFI_MTFTP4_OPCODE_WRQ here.\r
+\r
+  @retval EFI_SUCCESS           The upload process has been started.\r
+  @retval Others                Failed to start the upload.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4WrqStart (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN UINT16                 Operation\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // The valid block number range are [0, 0xffff]. For example:\r
+  // the client sends an WRQ request to the server, the server\r
+  // ACK with an ACK0 to let client start transfer the first\r
+  // packet.\r
+  //\r
+  Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Mtftp4SendRequest (Instance);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
+}\r
+\r
+\r
+/**\r
+  Build then send a MTFTP data packet for the MTFTP upload session.\r
+\r
+  @param  Instance              The MTFTP upload session\r
+  @param  BlockNum              The block number to send\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to build the packet\r
+  @retval EFI_ABORTED           The consumer of this child directs to abort the\r
+                                transmission by return an error through\r
+                                PacketNeeded\r
+  @retval EFI_SUCCESS           The data is sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4WrqSendBlock (\r
+  IN MTFTP4_PROTOCOL        *Instance,\r
+  IN UINT16                 BlockNum\r
+  )\r
+{\r
+  EFI_MTFTP4_PACKET         *Packet;\r
+  EFI_MTFTP4_TOKEN          *Token;\r
+  NET_BUF                   *UdpPacket;\r
+  EFI_STATUS                Status;\r
+  UINT16                    DataLen;\r
+  UINT8                     *DataBuf;\r
+  UINT64                    Start;\r
+\r
+  //\r
+  // Allocate a buffer to hold the user data\r
+  //\r
+  UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);\r
+\r
+  if (UdpPacket == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet = (EFI_MTFTP4_PACKET *)NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);\r
+\r
+  Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);\r
+  Packet->Data.Block  = HTONS (BlockNum);\r
+\r
+  //\r
+  // Read the block from either the buffer or PacketNeeded callback\r
+  //\r
+  Token   = Instance->Token;\r
+  DataLen = Instance->BlkSize;\r
+\r
+  if (Token->Buffer != NULL) {\r
+    Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);\r
+\r
+    if (Token->BufferSize < Start + Instance->BlkSize) {\r
+      DataLen             = (UINT16) (Token->BufferSize - Start);\r
+      Instance->LastBlock = BlockNum;\r
+      Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);\r
+    }\r
+\r
+    if (DataLen > 0) {\r
+      NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
+      NetCopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);\r
+    }\r
+\r
+  } else {\r
+    //\r
+    // Get data from PacketNeeded\r
+    //\r
+    DataBuf = NULL;\r
+    Status  = Token->PacketNeeded (&Instance->Mtftp4, Token, &DataLen, &DataBuf);\r
+\r
+    if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {\r
+      if (DataBuf != NULL) {\r
+        gBS->FreePool (DataBuf);\r
+      }\r
+\r
+      Mtftp4SendError (\r
+        Instance,\r
+        EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
+        "User aborted the transfer"\r
+        );\r
+\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    if (DataLen < Instance->BlkSize) {\r
+      Instance->LastBlock = BlockNum;\r
+      Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);\r
+    }\r
+\r
+    if (DataLen > 0) {\r
+      NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
+      NetCopyMem (Packet->Data.Data, DataBuf, DataLen);\r
+      gBS->FreePool (DataBuf);\r
+    }\r
+  }\r
+\r
+  return Mtftp4SendPacket (Instance, UdpPacket);\r
+}\r
+\r
+\r
+/**\r
+  Function to handle received ACK packet. If the ACK number matches the\r
+  expected block number, and there are more data pending, send the next\r
+  block. Otherwise tell the caller that we are done.\r
+\r
+  @param  Instance              The MTFTP upload session\r
+  @param  Packet                The MTFTP packet received\r
+  @param  Len                   The packet length\r
+  @param  Completed             Return whether the upload has finished.\r
+\r
+  @retval EFI_SUCCESS           The ACK is successfully processed.\r
+  @retval EFI_TFTP_ERROR        The block number loops back.\r
+  @retval Others                Failed to transmit the next data packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4WrqHandleAck (\r
+  IN  MTFTP4_PROTOCOL       *Instance,\r
+  IN  EFI_MTFTP4_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  OUT BOOLEAN               *Completed\r
+  )\r
+{\r
+  UINT16                    AckNum;\r
+  INTN                      Expected;\r
+\r
+  *Completed  = FALSE;\r
+  AckNum      = NTOHS (Packet->Ack.Block[0]);\r
+  Expected    = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
+\r
+  ASSERT (Expected >= 0);\r
+\r
+  //\r
+  // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput\r
+  // restart receive.\r
+  //\r
+  if (Expected != AckNum) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Remove the acked block number, if this is the last block number,\r
+  // tell the Mtftp4WrqInput to finish the transfer. This is the last\r
+  // block number if the block range are empty..\r
+  //\r
+  Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum);\r
+\r
+  Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
+\r
+  if (Expected < 0) {\r
+    //\r
+    // The block range is empty. It may either because the the last\r
+    // block has been ACKed, or the sequence number just looped back,\r
+    // that is, there is more than 0xffff blocks.\r
+    //\r
+    if (Instance->LastBlock == AckNum) {\r
+      ASSERT (Instance->LastBlock >= 1);\r
+      *Completed = TRUE;\r
+      return EFI_SUCCESS;\r
+\r
+    } else {\r
+      Mtftp4SendError (\r
+        Instance,\r
+        EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
+        "Block number rolls back, not supported, try blksize option"\r
+        );\r
+\r
+      return EFI_TFTP_ERROR;\r
+    }\r
+  }\r
+\r
+  return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);\r
+}\r
+\r
+\r
+/**\r
+  Check whether the received OACK is valid. The OACK is valid\r
+  only if:\r
+  1. It only include options requested by us\r
+  2. It can only include a smaller block size\r
+  3. It can't change the proposed time out value.\r
+  4. Other requirements of the individal MTFTP options as required.s\r
+\r
+  @param  Reply                 The options included in the OACK\r
+  @param  Request               The options we requested\r
+\r
+  @return TRUE if the options included in OACK is valid, otherwise FALSE.\r
+\r
+**/\r
+BOOLEAN\r
+Mtftp4WrqOackValid (\r
+  IN MTFTP4_OPTION              *Reply,\r
+  IN MTFTP4_OPTION              *Request\r
+  )\r
+{\r
+  //\r
+  // It is invalid for server to return options we don't request\r
+  //\r
+  if ((Reply->Exist &~Request->Exist) != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Server can only specify a smaller block size to be used and\r
+  // return the timeout matches that requested.\r
+  //\r
+  if (((Reply->Exist & MTFTP4_BLKSIZE_EXIST) && (Reply->BlkSize > Request->BlkSize)) ||\r
+      ((Reply->Exist & MTFTP4_TIMEOUT_EXIST) && (Reply->Timeout != Request->Timeout))) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Function to handle the MTFTP OACK packet. It parses the packet's\r
+  options, and update the internal states of the session\r
+\r
+  @param  Instance              The MTFTP session\r
+  @param  Packet                The received OACK packet\r
+  @param  Len                   The length of the packet\r
+  @param  Completed             Whether the transmisson has completed. NOT used by\r
+                                this function.\r
+\r
+  @retval EFI_SUCCESS           The OACK process is OK\r
+  @retval EFI_TFTP_ERROR        Some error occured, and the session reset.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp4WrqHandleOack (\r
+  IN  MTFTP4_PROTOCOL       *Instance,\r
+  IN  EFI_MTFTP4_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  OUT BOOLEAN               *Completed\r
+  )\r
+{\r
+  MTFTP4_OPTION             Reply;\r
+  EFI_MTFTP4_PACKET         Bogus;\r
+  EFI_STATUS                Status;\r
+  INTN                      Expected;\r
+\r
+  *Completed = FALSE;\r
+\r
+  //\r
+  // Ignore the OACK if already started the upload\r
+  //\r
+  Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
+\r
+  if (Expected != 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Parse and validate the options from server\r
+  //\r
+  NetZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
+  Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);\r
+\r
+  if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {\r
+    //\r
+    // Don't send a MTFTP error packet when out of resource, it can\r
+    // only make it worse.\r
+    //\r
+    if (Status != EFI_OUT_OF_RESOURCES) {\r
+      Mtftp4SendError (\r
+        Instance,\r
+        EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,\r
+        "Mal-formated OACK packet"\r
+        );\r
+    }\r
+\r
+    return EFI_TFTP_ERROR;\r
+  }\r
+\r
+  if (Reply.BlkSize != 0) {\r
+    Instance->BlkSize = Reply.BlkSize;\r
+  }\r
+\r
+  if (Reply.Timeout != 0) {\r
+    Instance->Timeout = Reply.Timeout;\r
+  }\r
+\r
+  //\r
+  // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,\r
+  // which will start the transmission of the first data block.\r
+  //\r
+  Bogus.Ack.OpCode    = HTONS (EFI_MTFTP4_OPCODE_ACK);\r
+  Bogus.Ack.Block[0]  = 0;\r
+\r
+  return Mtftp4WrqHandleAck (Instance, &Bogus, sizeof (EFI_MTFTP4_ACK_HEADER), Completed);\r
+}\r
+\r
+\r
+/**\r
+  The input process routine for MTFTP upload.\r
+\r
+  @param  UdpPacket             The received MTFTP packet.\r
+  @param  Points                The local/remote access point\r
+  @param  IoStatus              The result of the packet receiving\r
+  @param  Context               Opaque parameter for the callback, which is the\r
+                                MTFTP session.\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+Mtftp4WrqInput (\r
+  IN NET_BUF                *UdpPacket,\r
+  IN UDP_POINTS             *Points,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  MTFTP4_PROTOCOL           *Instance;\r
+  EFI_MTFTP4_PACKET         *Packet;\r
+  BOOLEAN                   Completed;\r
+  EFI_STATUS                Status;\r
+  UINT32                    Len;\r
+  UINT16                    Opcode;\r
+\r
+  Instance = (MTFTP4_PROTOCOL *) Context;\r
+  NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);\r
+\r
+  Completed = FALSE;\r
+  Packet    = NULL;\r
+  Status    = EFI_SUCCESS;\r
+\r
+  if (EFI_ERROR (IoStatus)) {\r
+    Status = IoStatus;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  ASSERT (UdpPacket != NULL);\r
+\r
+  if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Client send initial request to server's listening port. Server\r
+  // will select a UDP port to communicate with the client.\r
+  //\r
+  if (Points->RemotePort != Instance->ConnectedPort) {\r
+    if (Instance->ConnectedPort != 0) {\r
+      goto ON_EXIT;\r
+    } else {\r
+      Instance->ConnectedPort = Points->RemotePort;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
+  //\r
+  Len = UdpPacket->TotalSize;\r
+\r
+  if (UdpPacket->BlockOpNum > 1) {\r
+    Packet = NetAllocatePool (Len);\r
+\r
+    if (Packet == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
+\r
+  } else {\r
+    Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
+  }\r
+\r
+  Opcode = NTOHS (Packet->OpCode);\r
+\r
+  //\r
+  // Call the user's CheckPacket if provided. Abort the transmission\r
+  // if CheckPacket returns an EFI_ERROR code.\r
+  //\r
+  if ((Instance->Token->CheckPacket != NULL) &&\r
+      ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {\r
+\r
+    Status = Instance->Token->CheckPacket (\r
+                                &Instance->Mtftp4,\r
+                                Instance->Token,\r
+                                (UINT16) Len,\r
+                                Packet\r
+                                );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Send an error message to the server to inform it\r
+      //\r
+      if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {\r
+        Mtftp4SendError (\r
+          Instance,\r
+          EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
+          "User aborted the transfer"\r
+          );\r
+      }\r
+\r
+      Status = EFI_ABORTED;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  switch (Opcode) {\r
+  case EFI_MTFTP4_OPCODE_ACK:\r
+    if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);\r
+    break;\r
+\r
+  case EFI_MTFTP4_OPCODE_OACK:\r
+    if (Len <= MTFTP4_OPCODE_LEN) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);\r
+    break;\r
+\r
+  case EFI_MTFTP4_OPCODE_ERROR:\r
+    Status = EFI_TFTP_ERROR;\r
+    break;\r
+  }\r
+\r
+ON_EXIT:\r
+  //\r
+  // Free the resources, then if !EFI_ERROR (Status) and not completed,\r
+  // restart the receive, otherwise end the session.\r
+  //\r
+  if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {\r
+    NetFreePool (Packet);\r
+  }\r
+\r
+  if (UdpPacket != NULL) {\r
+    NetbufFree (UdpPacket);\r
+  }\r
+\r
+  if (!EFI_ERROR (Status) && !Completed) {\r
+    Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
+  }\r
+\r
+  //\r
+  // Status may have been updated by UdpIoRecvDatagram\r
+  //\r
+  if (EFI_ERROR (Status) || Completed) {\r
+    Mtftp4CleanOperation (Instance, Status);\r
+  }\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c
new file mode 100644 (file)
index 0000000..5f15c81
--- /dev/null
@@ -0,0 +1,2401 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  bc.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Bc.h"\r
+\r
+//\r
+//\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN  EFI_HANDLE                     Controller,\r
+  IN  UINTN                          NumberOfChildren,\r
+  IN  EFI_HANDLE                     *ChildHandleBuffer\r
+  );\r
+\r
+extern\r
+VOID\r
+InitArpHeader (\r
+  VOID\r
+  );\r
+extern\r
+VOID\r
+OptionsStrucInit (\r
+  VOID\r
+  );\r
+\r
+//\r
+// helper routines\r
+//\r
+\r
+/**\r
+  Convert number to ASCII value\r
+\r
+  @param  Number               Numeric value to convert to decimal ASCII value.\r
+  @param  Buffer               Buffer to place ASCII version of the Number\r
+  @param  Length               Length of Buffer.\r
+\r
+  @retval none                 none\r
+\r
+**/\r
+VOID\r
+CvtNum (\r
+  IN UINTN  Number,\r
+  IN UINT8  *Buffer,\r
+  IN INTN   Length\r
+  )\r
+{\r
+  UINTN Remainder;\r
+\r
+  while (Length--) {\r
+    Remainder = Number % 10;\r
+    Number /= 10;\r
+    Buffer[Length] = (UINT8) ('0' + Remainder);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Convert number to decimal ASCII value at Buffer location\r
+\r
+  @param  Number               Numeric value to convert to decimal ASCII value.\r
+  @param  Buffer               Buffer to place ASCII version of the Number\r
+\r
+  @retval none                 none\r
+\r
+**/\r
+VOID\r
+UtoA10 (\r
+  IN UINTN Number,\r
+  IN UINT8 *Buffer\r
+  )\r
+{\r
+  INTN  Index;\r
+  UINT8 BuffArray[31];\r
+\r
+  BuffArray[30] = 0;\r
+  CvtNum (Number, BuffArray, 30);\r
+\r
+  for (Index = 0; Index < 30; ++Index) {\r
+    if (BuffArray[Index] != '0') {\r
+      break;\r
+    }\r
+  }\r
+\r
+  CopyMem (Buffer, BuffArray + Index, 31 - Index);\r
+}\r
+\r
+\r
+/**\r
+  Convert ASCII numeric string to a UINTN value\r
+\r
+  @param  Number               Numeric value to convert to decimal ASCII value.\r
+  @param  Buffer               Buffer to place ASCII version of the Number\r
+\r
+  @retval Value                UINTN value of the ASCII string.\r
+\r
+**/\r
+UINTN\r
+AtoU (\r
+  IN UINT8 *Buffer\r
+  )\r
+{\r
+  UINTN Value;\r
+  INT8  Character;\r
+\r
+  Value     = 0;\r
+  Character = *Buffer++;\r
+  do {\r
+    Value     = Value * 10 + Character - '0';\r
+    Character = *Buffer++;\r
+  } while (Character);\r
+\r
+  return Value;\r
+}\r
+\r
+\r
+/**\r
+  Convert ASCII numeric string to a UINTN value\r
+\r
+  @param  Number               Numeric value to convert to decimal ASCII value.\r
+  @param  Buffer               Buffer to place ASCII version of the Number\r
+\r
+  @retval Value                UINTN value of the ASCII string.\r
+\r
+**/\r
+UINT64\r
+AtoU64 (\r
+  IN UINT8 *Buffer\r
+  )\r
+{\r
+  UINT64  Value;\r
+  UINT8   Character;\r
+\r
+  Value = 0;\r
+  while ((Character = *Buffer++) != '\0') {\r
+    Value = MultU64x32 (Value, 10) + (Character - '0');\r
+  }\r
+\r
+  return Value;\r
+}\r
+//\r
+// random number generator\r
+//\r
+#define RANDOM_MULTIPLIER   2053\r
+#define RANDOM_ADD_IN_VALUE 19\r
+\r
+VOID\r
+SeedRandom (\r
+  IN PXE_BASECODE_DEVICE  *Private,\r
+  IN UINT16               InitialSeed\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Initialize the Seed for the random number generator\r
+\r
+  Arguments:\r
+\r
+  Returns:\r
+    none                -\r
+\r
+--*/\r
+{\r
+  if (Private != NULL) {\r
+    Private->RandomSeed = InitialSeed;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Generate and return a pseudo-random number\r
+\r
+\r
+  @retval Number               UINT16 random number\r
+\r
+**/\r
+UINT16\r
+Random (\r
+  IN PXE_BASECODE_DEVICE  *Private\r
+  )\r
+{\r
+  UINTN Number;\r
+\r
+  if (Private != NULL) {\r
+    Number = -(INTN) Private->RandomSeed * RANDOM_MULTIPLIER + RANDOM_ADD_IN_VALUE;\r
+\r
+    return Private->RandomSeed = (UINT16) Number;\r
+  } else {\r
+    return 0;\r
+  }\r
+}\r
+//\r
+// calculate the internet checksum (RFC 1071)\r
+// return 16 bit ones complement of ones complement sum of 16 bit words\r
+//\r
+\r
+/**\r
+  Calculate the internet checksum (see RFC 1071)\r
+\r
+  @param  Packet               Buffer which contains the data to be checksummed\r
+  @param  Length               Length to be checksummed\r
+\r
+  @retval Checksum             Returns the 16 bit ones complement of  ones\r
+                               complement sum of 16 bit words\r
+\r
+**/\r
+UINT16\r
+IpChecksum (\r
+  IN UINT16 *Packet,\r
+  IN UINTN  Length\r
+  )\r
+{\r
+  UINT32  Sum;\r
+  UINT8   Odd;\r
+\r
+  Sum = 0;\r
+  Odd = (UINT8) (Length & 1);\r
+  Length >>= 1;\r
+  while (Length--) {\r
+    Sum += *Packet++;\r
+  }\r
+\r
+  if (Odd) {\r
+    Sum += *(UINT8 *) Packet;\r
+  }\r
+\r
+  Sum = (Sum & 0xffff) + (Sum >> 16);\r
+  //\r
+  // in case above carried\r
+  //\r
+  Sum += Sum >> 16;\r
+\r
+  return (UINT16) (~ (UINT16) Sum);\r
+}\r
+\r
+\r
+/**\r
+  Calculate the internet checksum (see RFC 1071)\r
+  on a non contiguous header and data\r
+\r
+  @param  Header               Buffer which contains the data to be checksummed\r
+  @param  HeaderLen            Length to be checksummed\r
+  @param  Message              Buffer which contains the data to be checksummed\r
+  @param  MessageLen           Length to be checksummed\r
+\r
+  @retval Checksum             Returns the 16 bit ones complement of  ones\r
+                               complement sum of 16 bit words\r
+\r
+**/\r
+UINT16\r
+IpChecksum2 (\r
+  IN UINT16 *Header,\r
+  IN UINTN  HeaderLen,\r
+  IN UINT16 *Message,\r
+  IN UINTN  MessageLen\r
+  )\r
+{\r
+  UINT32  Sum;\r
+\r
+  Sum = (UINT16)~IpChecksum (Header, HeaderLen) + (UINT16)~IpChecksum (Message, MessageLen);\r
+\r
+  //\r
+  // in case above carried\r
+  //\r
+  Sum += Sum >> 16;\r
+\r
+  return (UINT16) (~ (UINT16) Sum);\r
+}\r
+\r
+\r
+/**\r
+  Adjust the internet checksum (see RFC 1071) on a single word update.\r
+\r
+  @param  OldChkSum            Checksum previously calculated\r
+  @param  OldWord              Value\r
+  @param  NewWord              New Value\r
+\r
+  @retval Checksum             Returns the 16 bit ones complement of  ones\r
+                               complement sum of 16 bit words\r
+\r
+**/\r
+UINT16\r
+UpdateChecksum (\r
+  IN UINT16 OldChksum,\r
+  IN UINT16 OldWord,\r
+  IN UINT16 NewWord\r
+  )\r
+{\r
+  UINT32  sum;\r
+\r
+  sum = ~OldChksum + NewWord - OldWord;\r
+  //\r
+  // in case above carried\r
+  //\r
+  sum += sum >> 16;\r
+  return (UINT16) (~ (UINT16) sum);\r
+}\r
+\r
+\r
+/**\r
+  See if a callback is in play\r
+\r
+  @param  Private              Pointer to Pxe BaseCode Protocol\r
+\r
+  @retval 0                    Callbacks are active on the handle\r
+  @retval 1                    Callbacks are not active on the handle\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+SetMakeCallback (\r
+  IN PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  Private->EfiBc.Mode->MakeCallbacks = (BOOLEAN) (gBS->HandleProtocol (\r
+                                                        Private->Handle,\r
+                                                        &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+                                                        (VOID *) &Private->CallbackProtocolPtr\r
+                                                        ) == EFI_SUCCESS);\r
+\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nMode->MakeCallbacks == %d  ",\r
+    Private->EfiBc.Mode->MakeCallbacks)\r
+    );\r
+\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nPrivate->CallbackProtocolPtr == %xh  ",\r
+    Private->CallbackProtocolPtr)\r
+    );\r
+\r
+  if (Private->CallbackProtocolPtr != NULL) {\r
+    DEBUG (\r
+      (DEBUG_INFO,\r
+      "\nCallbackProtocolPtr->Revision = %xh  ",\r
+      Private->CallbackProtocolPtr->Revision)\r
+      );\r
+\r
+    DEBUG (\r
+      (DEBUG_INFO,\r
+      "\nCallbackProtocolPtr->Callback = %xh  ",\r
+      Private->CallbackProtocolPtr->Callback)\r
+      );\r
+  }\r
+\r
+  return Private->EfiBc.Mode->MakeCallbacks;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+  Routine which does an SNP->Receive over a timeout period and doing callbacks\r
+\r
+  @param  Private              Pointer to Pxe BaseCode Protocol\r
+  @param  Function             What PXE function to callback\r
+  @param  TimeoutEvent         Timer event that will trigger when we have waited\r
+                               too  long for an incoming packet\r
+  @param  HeaderSizePtr        Pointer to the size of the Header size\r
+  @param  BufferSizePtr        Pointer to the size of the Buffer size\r
+  @param  ProtocolPtr          The protocol to sniff for (namely, UDP/TCP/etc)\r
+\r
+  @retval 0                    Something was returned\r
+  @retval !0                   Like there was nothing to receive\r
+                               (EFI_TIMEOUT/NOT_READY)\r
+\r
+**/\r
+EFI_STATUS\r
+WaitForReceive (\r
+  IN PXE_BASECODE_DEVICE        *Private,\r
+  IN EFI_PXE_BASE_CODE_FUNCTION Function,\r
+  IN EFI_EVENT                  TimeoutEvent,\r
+  IN OUT UINTN                  *HeaderSizePtr,\r
+  IN OUT UINTN                  *BufferSizePtr,\r
+  IN OUT UINT16                 *ProtocolPtr\r
+  )\r
+{\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;\r
+  EFI_PXE_CALLBACK            CallbackPtr;\r
+  EFI_STATUS                  StatCode;\r
+  EFI_EVENT                   CallbackEvent;\r
+\r
+  //\r
+  // Initialize pointer to SNP interface\r
+  //\r
+  SnpPtr = Private->SimpleNetwork;\r
+\r
+  //\r
+  // Initialize pointer to PxeBc callback routine - if any\r
+  //\r
+  CallbackPtr = (Private->EfiBc.Mode->MakeCallbacks) ? Private->CallbackProtocolPtr->Callback : NULL;\r
+\r
+  //\r
+  // Create callback event and set timer\r
+  //\r
+  StatCode = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &CallbackEvent\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // every 100 milliseconds\r
+  //\r
+  StatCode = gBS->SetTimer (\r
+                    CallbackEvent,\r
+                    TimerPeriodic,\r
+                    1000000\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    gBS->CloseEvent (CallbackEvent);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Loop until a packet is received or a receive error is detected or\r
+  // a callback abort is detected or a timeout event occurs.\r
+  //\r
+  for (;;)\r
+  {\r
+    //\r
+    // Poll for received packet.\r
+    //\r
+    *BufferSizePtr = BUFFER_ALLOCATE_SIZE;\r
+\r
+    StatCode = SnpPtr->Receive (\r
+                        SnpPtr,\r
+                        HeaderSizePtr,\r
+                        BufferSizePtr,\r
+                        Private->ReceiveBufferPtr,\r
+                        0,\r
+                        0,\r
+                        ProtocolPtr\r
+                        );\r
+\r
+    if (!EFI_ERROR (StatCode)) {\r
+      //\r
+      // Packet was received.  Make received callback then return.\r
+      //\r
+      if (CallbackPtr != NULL) {\r
+        StatCode = CallbackPtr (\r
+                    Private->CallbackProtocolPtr,\r
+                    Function,\r
+                    TRUE,\r
+                    (UINT32) *BufferSizePtr,\r
+                    (EFI_PXE_BASE_CODE_PACKET *) Private->ReceiveBufferPtr\r
+                    );\r
+\r
+        if (StatCode != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+          StatCode = EFI_ABORTED;\r
+        } else {\r
+          StatCode = EFI_SUCCESS;\r
+        }\r
+      }\r
+\r
+      break;\r
+    }\r
+\r
+    if (StatCode != EFI_NOT_READY) {\r
+      break;\r
+    }\r
+    //\r
+    // Check for callback event.\r
+    //\r
+    if (!EFI_ERROR (gBS->CheckEvent (CallbackEvent))) {\r
+      //\r
+      // Make periodic callback if callback pointer is initialized.\r
+      //\r
+      if (CallbackPtr != NULL) {\r
+        StatCode = CallbackPtr (\r
+                    Private->CallbackProtocolPtr,\r
+                    Function,\r
+                    FALSE,\r
+                    0,\r
+                    NULL\r
+                    );\r
+\r
+        //\r
+        // Abort if directed to by callback routine.\r
+        //\r
+        if (StatCode != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+          StatCode = EFI_ABORTED;\r
+          break;\r
+        }\r
+      }\r
+    }\r
+    //\r
+    // Check for timeout event.\r
+    //\r
+    if (TimeoutEvent == 0) {\r
+      StatCode = EFI_TIMEOUT;\r
+      break;\r
+    }\r
+\r
+    if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+      StatCode = EFI_TIMEOUT;\r
+      break;\r
+    }\r
+    //\r
+    // Check IGMP timer events.\r
+    //\r
+    IgmpCheckTimers (Private);\r
+  }\r
+\r
+  gBS->CloseEvent (CallbackEvent);\r
+\r
+  return StatCode;\r
+}\r
+\r
+\r
+/**\r
+  Routine which does an SNP->Transmit of a buffer\r
+\r
+  @param  Private              Pointer to Pxe BaseCode Protocol\r
+  @param  HeaderPtr            Pointer to the buffer\r
+  @param  PacketPtr            Pointer to the packet to send\r
+  @param  PacketLen            The length of the entire packet to send\r
+  @param  HardwareAddr         Pointer to the MAC address of the destination\r
+  @param  MediaProtocol        What type of frame to create (RFC 1700) - IE.\r
+                               Ethernet\r
+  @param  Function             What PXE function to callback\r
+\r
+  @retval 0                    Something was sent\r
+  @retval !0                   An error was encountered during sending of a packet\r
+\r
+**/\r
+EFI_STATUS\r
+SendPacket (\r
+  PXE_BASECODE_DEVICE           *Private,\r
+  VOID                          *HeaderPtr,\r
+  VOID                          *PacketPtr,\r
+  INTN                          PacketLen,\r
+  VOID                          *HardwareAddr,\r
+  UINT16                        MediaProtocol,\r
+  IN EFI_PXE_BASE_CODE_FUNCTION Function\r
+  )\r
+{\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;\r
+  EFI_SIMPLE_NETWORK_MODE     *SnpModePtr;\r
+  EFI_PXE_CALLBACK            CallbackPtr;\r
+  EFI_STATUS                  StatCode;\r
+  EFI_EVENT                   TimeoutEvent;\r
+  UINT32                      IntStatus;\r
+  VOID                        *TxBuf;\r
+\r
+  //\r
+  //\r
+  //\r
+  CallbackPtr = Private->EfiBc.Mode->MakeCallbacks ? Private->CallbackProtocolPtr->Callback : 0;\r
+\r
+  SnpPtr      = Private->SimpleNetwork;\r
+  SnpModePtr  = SnpPtr->Mode;\r
+\r
+  //\r
+  // clear prior interrupt status\r
+  //\r
+  StatCode = SnpPtr->GetStatus (SnpPtr, &IntStatus, 0);\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nSendPacket()  Exit #1  %xh (%r)",\r
+      StatCode,\r
+      StatCode)\r
+      );\r
+    return StatCode;\r
+  }\r
+\r
+  Private->DidTransmit = FALSE;\r
+\r
+  if (CallbackPtr != NULL) {\r
+    if (CallbackPtr (\r
+          Private->CallbackProtocolPtr,\r
+          Function,\r
+          FALSE,\r
+          (UINT32) PacketLen,\r
+          PacketPtr\r
+          ) != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nSendPacket()  Exit #2  %xh (%r)",\r
+        EFI_ABORTED,\r
+        EFI_ABORTED)\r
+        );\r
+      return EFI_ABORTED;\r
+    }\r
+  }\r
+  //\r
+  // put packet in transmit queue\r
+  // headersize should be zero if not filled in\r
+  //\r
+  StatCode = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &TimeoutEvent\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    DEBUG (\r
+      (DEBUG_ERROR,\r
+      "Could not create transmit timeout event.  %r\n",\r
+      StatCode)\r
+      );\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // 5 milliseconds\r
+  //\r
+  StatCode = gBS->SetTimer (\r
+                    TimeoutEvent,\r
+                    TimerRelative,\r
+                    50000\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    DEBUG (\r
+      (DEBUG_ERROR,\r
+      "Could not set transmit timeout event timer.  %r\n",\r
+      StatCode)\r
+      );\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  for (;;) {\r
+    StatCode = SnpPtr->Transmit (\r
+                        SnpPtr,\r
+                        (UINTN) SnpPtr->Mode->MediaHeaderSize,\r
+                        (UINTN) (PacketLen + SnpPtr->Mode->MediaHeaderSize),\r
+                        HeaderPtr,\r
+                        &SnpModePtr->CurrentAddress,\r
+                        (EFI_MAC_ADDRESS *) HardwareAddr,\r
+                        &MediaProtocol\r
+                        );\r
+\r
+    if (StatCode != EFI_NOT_READY) {\r
+      break;\r
+    }\r
+\r
+    if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+      StatCode = EFI_TIMEOUT;\r
+      break;\r
+    }\r
+  }\r
+\r
+  gBS->CloseEvent (TimeoutEvent);\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nSendPacket()  Exit #3  %xh (%r)",\r
+      StatCode,\r
+      StatCode)\r
+      );\r
+    return StatCode;\r
+  }\r
+  //\r
+  // remove transmit buffer from snp's unused queue\r
+  // done this way in case someday things are buffered and we don't get it back\r
+  // immediately\r
+  //\r
+  StatCode = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &TimeoutEvent\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    DEBUG (\r
+      (DEBUG_ERROR,\r
+      "Could not create transmit status timeout event.  %r\n",\r
+      StatCode)\r
+      );\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // 5 milliseconds\r
+  //\r
+  StatCode = gBS->SetTimer (\r
+                    TimeoutEvent,\r
+                    TimerRelative,\r
+                    50000\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    DEBUG (\r
+      (DEBUG_ERROR,\r
+      "Could not set transmit status timeout event timer.  %r\n",\r
+      StatCode)\r
+      );\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  for (;;) {\r
+    StatCode = SnpPtr->GetStatus (SnpPtr, &IntStatus, &TxBuf);\r
+\r
+    if (EFI_ERROR (StatCode)) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nSendPacket()  Exit #4  %xh (%r)",\r
+        StatCode,\r
+        StatCode)\r
+        );\r
+      break;\r
+    }\r
+\r
+    if (IntStatus & EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT) {\r
+      Private->DidTransmit = TRUE;\r
+    }\r
+\r
+    if (TxBuf != NULL) {\r
+      break;\r
+    }\r
+\r
+    if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+      StatCode = EFI_TIMEOUT;\r
+      break;\r
+    }\r
+  }\r
+\r
+  gBS->CloseEvent (TimeoutEvent);\r
+\r
+  return StatCode;\r
+}\r
+//\r
+//\r
+//\r
+\r
+/**\r
+\r
+\r
+**/\r
+EFI_BIS_PROTOCOL *\r
+PxebcBisStart (\r
+  IN PXE_BASECODE_DEVICE      *Private,\r
+  OUT BIS_APPLICATION_HANDLE  *BisAppHandle,\r
+  OUT OPTIONAL EFI_BIS_DATA            **BisDataSigInfo\r
+  )\r
+{\r
+  EFI_STATUS        EfiStatus;\r
+  EFI_HANDLE        BisHandleBuffer;\r
+  UINTN             BisHandleCount;\r
+  EFI_BIS_PROTOCOL  *BisPtr;\r
+  EFI_BIS_VERSION   BisInterfaceVersion;\r
+  BOOLEAN           BisCheckFlag;\r
+\r
+  BisHandleCount  = sizeof (EFI_HANDLE);\r
+  BisCheckFlag    = FALSE;\r
+\r
+  //\r
+  // Locate BIS protocol handle (if present).\r
+  // If BIS protocol handle is not found, return NULL.\r
+  //\r
+  DEBUG ((DEBUG_INFO, "\ngBS->LocateHandle()  "));\r
+\r
+  EfiStatus = gBS->LocateHandle (\r
+                    ByProtocol,\r
+                    &gEfiBisProtocolGuid,\r
+                    NULL,\r
+                    &BisHandleCount,\r
+                    &BisHandleBuffer\r
+                    );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    //\r
+    // Any error means that there is no BIS.\r
+    // Note - It could mean that there are more than\r
+    // one BIS protocols installed, but that scenario\r
+    // is not yet supported.\r
+    //\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nPxebcBisStart()""\n  gBS->LocateHandle()  %r (%xh)\n",\r
+      EfiStatus,\r
+      EfiStatus)\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  if (BisHandleCount != sizeof BisHandleBuffer) {\r
+    //\r
+    // This really should never happen, but I am paranoid.\r
+    //\r
+    DEBUG (\r
+      (DEBUG_NET,\r
+      "\nPxebcBisStart()  BisHandleCount != %d\n",\r
+      sizeof BisHandleBuffer)\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "BIS handle found."));\r
+\r
+  //\r
+  // Locate BIS protocol interface.\r
+  // If the BIS protocol interface cannot be found, return NULL.\r
+  //\r
+  DEBUG ((DEBUG_INFO, "\ngBS->HandleProtocol()  "));\r
+\r
+  EfiStatus = gBS->HandleProtocol (\r
+                    BisHandleBuffer,\r
+                    &gEfiBisProtocolGuid,\r
+                    (VOID **) &BisPtr\r
+                    );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nPxebcBisStart()""\n  gBS->HandleProtocol()  %r (%xh)\n",\r
+      EfiStatus,\r
+      EfiStatus)\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  if (BisPtr == NULL) {\r
+    //\r
+    // This really should never happen.\r
+    //\r
+    DEBUG (\r
+      (DEBUG_NET,\r
+      "\nPxebcBisStart()""\n  gBS->HandleProtocoL()  ""BIS protocol interface pointer is NULL!\n")\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "BIS protocol interface found."));\r
+\r
+  //\r
+  // Check that all of the BIS API function pointers are not NULL.\r
+  //\r
+  if (BisPtr->Initialize == NULL ||\r
+      BisPtr->Shutdown == NULL ||\r
+      BisPtr->Free == NULL ||\r
+      BisPtr->GetBootObjectAuthorizationCertificate == NULL ||\r
+      BisPtr->GetBootObjectAuthorizationCheckFlag == NULL ||\r
+      BisPtr->GetBootObjectAuthorizationUpdateToken == NULL ||\r
+      BisPtr->GetSignatureInfo == NULL ||\r
+      BisPtr->UpdateBootObjectAuthorization == NULL ||\r
+      BisPtr->VerifyBootObject == NULL ||\r
+      BisPtr->VerifyObjectWithCredential == NULL\r
+      ) {\r
+    DEBUG (\r
+      (\r
+      DEBUG_NET,\r
+      "\nPxebcBisStart()""\n  BIS protocol interface is invalid."\r
+      "\n  At least one BIS protocol function pointer is NULL.\n"\r
+      )\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+  //\r
+  // Initialize BIS.\r
+  // If BIS does not initialize, return NULL.\r
+  //\r
+  DEBUG ((DEBUG_INFO, "\nBisPtr->Initialize()  "));\r
+\r
+  BisInterfaceVersion.Major = BIS_VERSION_1;\r
+\r
+  EfiStatus = BisPtr->Initialize (\r
+                        BisPtr,\r
+                        BisAppHandle,\r
+                        &BisInterfaceVersion,\r
+                        NULL\r
+                        );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nPxebcBisStart()""\n  BisPtr->Initialize()  %r (%xh)\n",\r
+      EfiStatus,\r
+      EfiStatus)\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "  BIS version: %d.%d",\r
+    BisInterfaceVersion.Major,\r
+    BisInterfaceVersion.Minor)\r
+    );\r
+\r
+  //\r
+  // If the requested BIS API version is not supported,\r
+  // shutdown BIS and return NULL.\r
+  //\r
+  if (BisInterfaceVersion.Major != BIS_VERSION_1) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nPxebcBisStart()""\n  BIS version %d.%d not supported by PXE BaseCode.\n",\r
+      BisInterfaceVersion.Major,\r
+      BisInterfaceVersion.Minor)\r
+      );\r
+\r
+    BisPtr->Shutdown (*BisAppHandle);\r
+    return NULL;\r
+  }\r
+  //\r
+  // Get BIS check flag.\r
+  // If the BIS check flag cannot be read, shutdown BIS and return NULL.\r
+  //\r
+  DEBUG ((DEBUG_INFO, "\nBisPtr->GetBootObjectAuthorizationCheckFlag()  "));\r
+\r
+  EfiStatus = BisPtr->GetBootObjectAuthorizationCheckFlag (*BisAppHandle, &BisCheckFlag);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nPxebcBisStart()""\n  BisPtr->GetBootObjectAuthorizationCheckFlag()  %r (%xh)\n",\r
+      EfiStatus,\r
+      EfiStatus)\r
+      );\r
+\r
+    BisPtr->Shutdown (*BisAppHandle);\r
+    return NULL;\r
+  }\r
+  //\r
+  // If the BIS check flag is FALSE, shutdown BIS and return NULL.\r
+  //\r
+  if (!BisCheckFlag) {\r
+    DEBUG ((DEBUG_INFO, "\nBIS check flag is FALSE.\n"));\r
+    BisPtr->Shutdown (*BisAppHandle);\r
+    return NULL;\r
+  } else {\r
+    DEBUG ((DEBUG_INFO, "\nBIS check flag is TRUE."));\r
+  }\r
+  //\r
+  // Early out if caller does not want signature information.\r
+  //\r
+  if (BisDataSigInfo == NULL) {\r
+    return BisPtr;\r
+  }\r
+  //\r
+  // Get BIS signature information.\r
+  // If the signature information cannot be read or is invalid,\r
+  // shutdown BIS and return NULL.\r
+  //\r
+  DEBUG ((DEBUG_INFO, "\nBisPtr->GetSignatureInfo()  "));\r
+\r
+  EfiStatus = BisPtr->GetSignatureInfo (*BisAppHandle, BisDataSigInfo);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nPxebcBisStart()""\n  BisPtr_GetSignatureInfo()  %r (%xh)\n",\r
+      EfiStatus,\r
+      EfiStatus)\r
+      );\r
+\r
+    BisPtr->Shutdown (*BisAppHandle);\r
+    return NULL;\r
+  }\r
+\r
+  if (*BisDataSigInfo == NULL) {\r
+    //\r
+    // This should never happen.\r
+    //\r
+    DEBUG (\r
+      (DEBUG_NET,\r
+      "\nPxebcBisStart()""\n  BisPtr->GetSignatureInfo()  Data pointer is NULL!\n")\r
+      );\r
+\r
+    BisPtr->Shutdown (*BisAppHandle);\r
+    return NULL;\r
+  }\r
+\r
+  if ((*BisDataSigInfo)->Length < sizeof (EFI_BIS_SIGNATURE_INFO) ||\r
+      (*BisDataSigInfo)->Length % sizeof (EFI_BIS_SIGNATURE_INFO) ||\r
+      (*BisDataSigInfo)->Length > sizeof (EFI_BIS_SIGNATURE_INFO) * 63\r
+      ) {\r
+    //\r
+    // This should never happen.\r
+    //\r
+    DEBUG (\r
+      (DEBUG_NET,\r
+      "\nPxebcBisStart()""\n  BisPtr->GetSignatureInfo()  Invalid BIS siginfo length.\n")\r
+      );\r
+\r
+    BisPtr->Free (*BisAppHandle, *BisDataSigInfo);\r
+    BisPtr->Shutdown (*BisAppHandle);\r
+    return NULL;\r
+  }\r
+\r
+  return BisPtr;\r
+}\r
+\r
+\r
+/**\r
+\r
+\r
+**/\r
+VOID\r
+PxebcBisStop (\r
+  EFI_BIS_PROTOCOL        *BisPtr,\r
+  BIS_APPLICATION_HANDLE  BisAppHandle,\r
+  EFI_BIS_DATA            *BisDataSigInfo\r
+  )\r
+{\r
+  if (BisPtr == NULL) {\r
+    return ;\r
+  }\r
+  //\r
+  // Free BIS allocated resources and shutdown BIS.\r
+  // Return TRUE - BIS support is officially detected.\r
+  //\r
+  if (BisDataSigInfo != NULL) {\r
+    BisPtr->Free (BisAppHandle, BisDataSigInfo);\r
+  }\r
+\r
+  BisPtr->Shutdown (BisAppHandle);\r
+}\r
+\r
+\r
+/**\r
+\r
+  @return TRUE := verified\r
+  @return FALSE := not verified\r
+\r
+**/\r
+BOOLEAN\r
+PxebcBisVerify (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  VOID                *FileBuffer,\r
+  UINTN               FileLength,\r
+  VOID                *CredentialBuffer,\r
+  UINTN               CredentialLength\r
+  )\r
+{\r
+  EFI_BIS_PROTOCOL        *BisPtr;\r
+  BIS_APPLICATION_HANDLE  BisAppHandle;\r
+  EFI_BIS_DATA            FileData;\r
+  EFI_BIS_DATA            CredentialData;\r
+  EFI_STATUS              EfiStatus;\r
+  BOOLEAN                 IsVerified;\r
+\r
+  if (Private == NULL || FileBuffer == NULL || FileLength == 0 || CredentialBuffer == NULL || CredentialLength == 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  BisPtr = PxebcBisStart (Private, &BisAppHandle, NULL);\r
+\r
+  if (BisPtr == NULL) {\r
+    return FALSE;\r
+  }\r
+\r
+  FileData.Length       = (UINT32) FileLength;\r
+  FileData.Data         = FileBuffer;\r
+  CredentialData.Length = (UINT32) CredentialLength;\r
+  CredentialData.Data   = CredentialBuffer;\r
+\r
+  EfiStatus = BisPtr->VerifyBootObject (\r
+                        BisAppHandle,\r
+                        &CredentialData,\r
+                        &FileData,\r
+                        &IsVerified\r
+                        );\r
+\r
+  PxebcBisStop (BisPtr, BisAppHandle, NULL);\r
+\r
+  return (BOOLEAN) ((EFI_ERROR (EfiStatus)) ? FALSE : (IsVerified ? TRUE : FALSE));\r
+}\r
+\r
+\r
+/**\r
+\r
+  @return TRUE := BIS present\r
+  @return FALSE := BIS not present\r
+\r
+**/\r
+BOOLEAN\r
+PxebcBisDetect (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  EFI_BIS_PROTOCOL        *BisPtr;\r
+  BIS_APPLICATION_HANDLE  BisAppHandle;\r
+  EFI_BIS_DATA            *BisDataSigInfo;\r
+\r
+  BisPtr = PxebcBisStart (Private, &BisAppHandle, &BisDataSigInfo);\r
+\r
+  if (BisPtr == NULL) {\r
+    return FALSE;\r
+  }\r
+\r
+  PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo);\r
+\r
+  return TRUE;\r
+}\r
+\r
+static VOID *BCNotifyReg;\r
+\r
+\r
+/**\r
+  Start and initialize the BaseCode protocol, Simple Network protocol and UNDI.\r
+\r
+  @param  Private              Pointer to Pxe BaseCode Protocol\r
+  @param  UseIPv6              Do we want to support IPv6?\r
+\r
+  @return EFI_SUCCESS\r
+  @return EFI_INVALID_PARAMETER\r
+  @return EFI_UNSUPPORTED\r
+  @return EFI_ALREADY_STARTED\r
+  @return EFI_OUT_OF_RESOURCES\r
+  @return Status is also returned from SNP.Start() and SNP.Initialize().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcStart (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+  IN BOOLEAN                    UseIPv6\r
+  )\r
+{\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;\r
+  EFI_SIMPLE_NETWORK_MODE     *SnpModePtr;\r
+  EFI_STATUS                  Status;\r
+  EFI_STATUS                  StatCode;\r
+  PXE_BASECODE_DEVICE         *Private;\r
+\r
+  //\r
+  // Lock the instance data\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  //\r
+  // Make sure BaseCode is not already started.\r
+  //\r
+  if (This->Mode->Started) {\r
+    DEBUG ((DEBUG_WARN, "\nBcStart()  BC is already started.\n"));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+#if !SUPPORT_IPV6\r
+  //\r
+  // Fail if IPv6 is requested and not supported.\r
+  //\r
+  if (UseIPv6) {\r
+    DEBUG ((DEBUG_WARN, "\nBcStart()  IPv6 is not supported.\n"));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+#endif\r
+  //\r
+  // Setup shortcuts to SNP protocol and data structure.\r
+  //\r
+  SnpPtr      = Private->SimpleNetwork;\r
+  SnpModePtr  = SnpPtr->Mode;\r
+\r
+  //\r
+  // Start and initialize SNP.\r
+  //\r
+  if (SnpModePtr->State == EfiSimpleNetworkStopped) {\r
+    StatCode = (*SnpPtr->Start) (SnpPtr);\r
+\r
+    if (SnpModePtr->State != EfiSimpleNetworkStarted) {\r
+      DEBUG ((DEBUG_WARN, "\nBcStart()  Could not start SNP.\n"));\r
+      EfiReleaseLock (&Private->Lock);\r
+      return StatCode;\r
+    }\r
+  }\r
+  //\r
+  // acquire memory for mode and transmit/receive buffers\r
+  //\r
+  if (SnpModePtr->State == EfiSimpleNetworkStarted) {\r
+    StatCode = (*SnpPtr->Initialize) (SnpPtr, 0, 0);\r
+\r
+    if (SnpModePtr->State != EfiSimpleNetworkInitialized) {\r
+      DEBUG ((DEBUG_WARN, "\nBcStart()  Could not initialize SNP."));\r
+      EfiReleaseLock (&Private->Lock);\r
+      return StatCode;\r
+    }\r
+  }\r
+  //\r
+  // Dump debug info.\r
+  //\r
+  DEBUG ((DEBUG_INFO, "\nBC Start()"));\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->State                    %Xh",\r
+    SnpModePtr->State)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->HwAddressSize            %Xh",\r
+    SnpModePtr->HwAddressSize)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->MediaHeaderSize          %Xh",\r
+    SnpModePtr->MediaHeaderSize)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->MaxPacketSize            %Xh",\r
+    SnpModePtr->MaxPacketSize)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->MacAddressChangeable     %Xh",\r
+    SnpModePtr->MacAddressChangeable)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->MultipleTxSupported      %Xh",\r
+    SnpModePtr->MultipleTxSupported)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->CurrentAddress           %Xh",\r
+     *((UINTN *)&SnpModePtr->CurrentAddress))\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->BroadcastAddress         %Xh",\r
+    *((UINTN *)&SnpModePtr->BroadcastAddress))\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->PermanentAddress         %Xh",\r
+    *((UINTN *)&SnpModePtr->PermanentAddress))\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->NvRamSize                %Xh",\r
+    SnpModePtr->NvRamSize)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->NvRamAccessSize          %Xh",\r
+    SnpModePtr->NvRamAccessSize)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->ReceiveFilterMask        %Xh",\r
+    SnpModePtr->ReceiveFilterMask)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->ReceiveFilterSetting     %Xh",\r
+    SnpModePtr->ReceiveFilterSetting)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->MCastFilterCount         %Xh",\r
+    SnpModePtr->MCastFilterCount)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->MCastFilter              %Xh",\r
+    SnpModePtr->MCastFilter)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->IfType                   %Xh",\r
+    SnpModePtr->IfType)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->MediaPresentSupported    %Xh",\r
+    SnpModePtr->MediaPresentSupported)\r
+    );\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nSnpModePtr->MediaPresent             %Xh",\r
+    SnpModePtr->MediaPresent)\r
+    );\r
+\r
+  //\r
+  // If media check is supported and there is no media,\r
+  // return error to caller.\r
+  //\r
+  if (SnpModePtr->MediaPresentSupported && !SnpModePtr->MediaPresent) {\r
+    DEBUG ((DEBUG_WARN, "\nBcStart()  Media not present.\n"));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NO_MEDIA;\r
+  }\r
+  //\r
+  // Allocate Tx/Rx buffers\r
+  //\r
+  Status = gBS->AllocatePool (\r
+                  EfiBootServicesData,\r
+                  BUFFER_ALLOCATE_SIZE,\r
+                  &Private->TransmitBufferPtr\r
+                  );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    ZeroMem (Private->TransmitBufferPtr, BUFFER_ALLOCATE_SIZE);\r
+  } else {\r
+    DEBUG ((DEBUG_NET, "\nBcStart()  Could not alloc TxBuf.\n"));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = gBS->AllocatePool (\r
+                  EfiBootServicesData,\r
+                  BUFFER_ALLOCATE_SIZE,\r
+                  &Private->ReceiveBufferPtr\r
+                  );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    ZeroMem (Private->ReceiveBufferPtr, BUFFER_ALLOCATE_SIZE);\r
+  } else {\r
+    DEBUG ((DEBUG_NET, "\nBcStart()  Could not alloc RxBuf.\n"));\r
+    gBS->FreePool (Private->TransmitBufferPtr);\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = gBS->AllocatePool (\r
+                  EfiBootServicesData,\r
+                  256,\r
+                  &Private->TftpErrorBuffer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->FreePool (Private->ReceiveBufferPtr);\r
+    gBS->FreePool (Private->TransmitBufferPtr);\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = gBS->AllocatePool (EfiBootServicesData, 256, &Private->TftpAckBuffer);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->FreePool (Private->TftpErrorBuffer);\r
+    gBS->FreePool (Private->ReceiveBufferPtr);\r
+    gBS->FreePool (Private->TransmitBufferPtr);\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Initialize private BaseCode instance data\r
+  //\r
+  do {\r
+    Private->RandomPort = (UINT16) (Private->RandomPort + PXE_RND_PORT_LOW + Random (Private));\r
+  } while (Private->RandomPort < PXE_RND_PORT_LOW);\r
+\r
+  Private->Igmpv1TimeoutEvent = NULL;\r
+  Private->UseIgmpv1Reporting = TRUE;\r
+  Private->IpLength           = IP_ADDRESS_LENGTH (Private->EfiBc.Mode);\r
+\r
+  //\r
+  // Initialize Mode structure\r
+  //\r
+  ZeroMem (Private->EfiBc.Mode, sizeof (EFI_PXE_BASE_CODE_MODE));\r
+  //\r
+  // check for callback protocol and set boolean\r
+  //\r
+  SetMakeCallback (Private);\r
+  Private->EfiBc.Mode->Started              = TRUE;\r
+  Private->EfiBc.Mode->TTL                  = DEFAULT_TTL;\r
+  Private->EfiBc.Mode->ToS                  = DEFAULT_ToS;\r
+  Private->EfiBc.Mode->UsingIpv6            = UseIPv6;\r
+\r
+  //\r
+  // Set to PXE_TRUE by the BC constructor if this BC implementation\r
+  // supports IPv6.\r
+  //\r
+  Private->EfiBc.Mode->Ipv6Supported = SUPPORT_IPV6;\r
+\r
+  Private->EfiBc.Mode->Ipv6Available = FALSE;\r
+  //\r
+  // Set to TRUE by the BC constructor if this BC implementation\r
+  // supports BIS.\r
+  //\r
+  Private->EfiBc.Mode->BisSupported = TRUE;\r
+  Private->EfiBc.Mode->BisDetected  = PxebcBisDetect (Private);\r
+\r
+  //\r
+  // This field is set to PXE_TRUE by the BC Start() function.  When this\r
+  // field is PXE_TRUE, ARP packets are sent as needed to get IP and MAC\r
+  // addresses.  This can cause unexpected delays in the DHCP(), Discover()\r
+  // and MTFTP() functions.  Setting this to PXE_FALSE will cause these\r
+  // functions to fail if the required IP/MAC information is not in the\r
+  // ARP cache.  The value of this field can be changed by an application\r
+  // at any time.\r
+  //\r
+  Private->EfiBc.Mode->AutoArp = TRUE;\r
+\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  EfiReleaseLock (&Private->Lock);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Stop the BaseCode protocol, Simple Network protocol and UNDI.\r
+\r
+  @param  Private              Pointer to Pxe BaseCode Protocol\r
+\r
+  @retval 0                    Successfully stopped\r
+  @retval !0                   Failed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcStop (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL *This\r
+  )\r
+{\r
+  //\r
+  // Lock the instance data\r
+  //\r
+  EFI_PXE_BASE_CODE_MODE      *PxebcMode;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;\r
+  EFI_SIMPLE_NETWORK_MODE     *SnpModePtr;\r
+  EFI_STATUS                  StatCode;\r
+  PXE_BASECODE_DEVICE         *Private;\r
+\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  PxebcMode   = Private->EfiBc.Mode;\r
+  SnpPtr      = Private->SimpleNetwork;\r
+  SnpModePtr  = SnpPtr->Mode;\r
+\r
+  //\r
+  // Issue BC command\r
+  //\r
+  StatCode = EFI_NOT_STARTED;\r
+\r
+  if (SnpModePtr->State == EfiSimpleNetworkInitialized) {\r
+    StatCode = (*SnpPtr->Shutdown) (SnpPtr);\r
+  }\r
+\r
+  if (SnpModePtr->State == EfiSimpleNetworkStarted) {\r
+    StatCode = (*SnpPtr->Stop) (SnpPtr);\r
+  }\r
+\r
+  if (Private->TransmitBufferPtr != NULL) {\r
+    gBS->FreePool (Private->TransmitBufferPtr);\r
+    Private->TransmitBufferPtr = NULL;\r
+  }\r
+\r
+  if (Private->ReceiveBufferPtr != NULL) {\r
+    gBS->FreePool (Private->ReceiveBufferPtr);\r
+    Private->ReceiveBufferPtr = NULL;\r
+  }\r
+\r
+  if (Private->ArpBuffer != NULL) {\r
+    gBS->FreePool (Private->ArpBuffer);\r
+    Private->ArpBuffer = NULL;\r
+  }\r
+\r
+  if (Private->TftpErrorBuffer != NULL) {\r
+    gBS->FreePool (Private->TftpErrorBuffer);\r
+    Private->TftpErrorBuffer = NULL;\r
+  }\r
+\r
+  if (Private->TftpAckBuffer != NULL) {\r
+    gBS->FreePool (Private->TftpAckBuffer);\r
+    Private->TftpAckBuffer = NULL;\r
+  }\r
+\r
+  if (Private->Igmpv1TimeoutEvent != NULL) {\r
+    gBS->CloseEvent (Private->Igmpv1TimeoutEvent);\r
+    Private->Igmpv1TimeoutEvent = NULL;\r
+  }\r
+\r
+  Private->FileSize             = 0;\r
+\r
+  if (!Private->EfiBc.Mode->Started) {\r
+    StatCode = EFI_NOT_STARTED;\r
+  } else {\r
+    Private->EfiBc.Mode->Started = FALSE;\r
+  }\r
+\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+\r
+const IPV4_ADDR AllSystemsGroup = { 224, 0, 0, 1 };\r
+\r
+\r
+/**\r
+  Set up the IP filter\r
+\r
+  @param  Private              Pointer to Pxe BaseCode Protocol\r
+  @param  Filter               Pointer to the filter\r
+\r
+  @retval 0                    Successfully set the filter\r
+  @retval !0                   Failed\r
+\r
+**/\r
+EFI_STATUS\r
+IpFilter (\r
+  IN PXE_BASECODE_DEVICE          *Private,\r
+  IN EFI_PXE_BASE_CODE_IP_FILTER  *Filter\r
+  )\r
+{\r
+  EFI_STATUS                  StatCode;\r
+  EFI_MAC_ADDRESS             MACadds[PXE_IP_FILTER_SIZE];\r
+  EFI_PXE_BASE_CODE_MODE      *PxebcMode;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;\r
+  EFI_SIMPLE_NETWORK_MODE     *SnpModePtr;\r
+  UINT32                      Enable;\r
+  UINT32                      Disable;\r
+  UINTN                       Index;\r
+  UINTN                       Index2;\r
+\r
+  PxebcMode   = Private->EfiBc.Mode;\r
+  SnpPtr      = Private->SimpleNetwork;\r
+  SnpModePtr  = SnpPtr->Mode;\r
+\r
+  //\r
+  // validate input parameters\r
+  // must have a filter\r
+  // must not have any extra filter bits set\r
+  //\r
+  if (Filter == NULL ||\r
+      (Filter->Filters &~FILTER_BITS)\r
+      //\r
+      // must not have a count which is too large or with no IP list\r
+      //\r
+      ||\r
+      (Filter->IpCnt && (!Filter->IpList || Filter->IpCnt > PXE_IP_FILTER_SIZE))\r
+      //\r
+      // must not have incompatible filters - promiscuous incompatible with anything else\r
+      //\r
+      ||\r
+      (\r
+        (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) &&\r
+      ((Filter->Filters &~EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) || Filter->IpCnt)\r
+    )\r
+      ) {\r
+    DEBUG ((DEBUG_INFO, "\nIpFilter()  Exit #1"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // promiscuous multicast incompatible with multicast in IP list\r
+  //\r
+  if (Filter->IpCnt && (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST)) {\r
+    for (Index = 0; Index < Filter->IpCnt; ++Index) {\r
+      if (IS_MULTICAST (&Filter->IpList[Index])) {\r
+        DEBUG ((DEBUG_INFO, "\nIpFilter()  Exit #2"));\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // leave groups for all those multicast which are no longer enabled\r
+  //\r
+  for (Index = 0; Index < PxebcMode->IpFilter.IpCnt; ++Index) {\r
+    if (!IS_MULTICAST (&PxebcMode->IpFilter.IpList[Index])) {\r
+      continue;\r
+    }\r
+\r
+    for (Index2 = 0; Index2 < Filter->IpCnt; ++Index2) {\r
+      if (!CompareMem (&PxebcMode->IpFilter.IpList[Index], &Filter->IpList[Index2], IP_ADDRESS_LENGTH (PxebcMode))) {\r
+        //\r
+        // still enabled\r
+        //\r
+        break;\r
+      }\r
+    }\r
+    //\r
+    // if we didn't find it, remove from group\r
+    //\r
+    if (Index2 == Filter->IpCnt) {\r
+      IgmpLeaveGroup (Private, &PxebcMode->IpFilter.IpList[Index]);\r
+    }\r
+  }\r
+  //\r
+  // set enable bits, convert multicast ip adds, join groups\r
+  // allways leave receive broadcast enabled at hardware layer\r
+  //\r
+  Index2 = 0;\r
+\r
+  if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) {\r
+    Enable = EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;\r
+  } else {\r
+    if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) {\r
+      Enable = EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;\r
+    } else {\r
+      Enable = EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;\r
+\r
+      for (Index = 0; Index < Filter->IpCnt; ++Index) {\r
+        PxebcMode->IpFilter.IpList[Index] = Filter->IpList[Index];\r
+\r
+        if (IS_MULTICAST (&Filter->IpList[Index])) {\r
+          EFI_IP_ADDRESS  *TmpIp;\r
+\r
+          Enable |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;\r
+\r
+          //\r
+          // if this is the first group, add the all systems group to mcast list\r
+          //\r
+          if (!Index2)\r
+          {\r
+              TmpIp = (EFI_IP_ADDRESS *) &AllSystemsGroup;\r
+            --Index;\r
+          } else {\r
+            TmpIp = (EFI_IP_ADDRESS *) &Filter->IpList[Index];\r
+          }\r
+          //\r
+          // get MAC address of IP\r
+          //\r
+          StatCode = (*SnpPtr->MCastIpToMac) (SnpPtr, PxebcMode->UsingIpv6, TmpIp, &MACadds[Index2++]);\r
+\r
+          if (EFI_ERROR (StatCode)) {\r
+            DEBUG (\r
+              (DEBUG_INFO,\r
+              "\nIpFilter()  Exit #2  %Xh (%r)",\r
+              StatCode,\r
+              StatCode)\r
+              );\r
+            return StatCode;\r
+          }\r
+        } else {\r
+          Enable |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;\r
+        }\r
+      }\r
+    }\r
+\r
+    if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) {\r
+      Enable |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;\r
+    }\r
+  }\r
+  //\r
+  // if nothing changed, just return\r
+  //\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nsnp->ReceiveFilterSetting == %Xh  Filter->IpCnt == %Xh",\r
+    SnpModePtr->ReceiveFilterSetting,\r
+    Filter->IpCnt)\r
+    );\r
+\r
+  if (SnpModePtr->ReceiveFilterSetting == Enable && !Filter->IpCnt) {\r
+    DEBUG ((DEBUG_INFO, "\nIpFilter()  Exit #4"));\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // disable those currently set but not set in new filter\r
+  //\r
+  Disable                   = SnpModePtr->ReceiveFilterSetting &~Enable;\r
+\r
+  StatCode                  = SnpPtr->ReceiveFilters (SnpPtr, Enable, Disable, FALSE, Index2, MACadds);\r
+\r
+  PxebcMode->IpFilter.IpCnt = Filter->IpCnt;\r
+\r
+  //\r
+  // join groups for all multicast in list\r
+  //\r
+  for (Index = 0; Index < Filter->IpCnt; ++Index) {\r
+    if (IS_MULTICAST (&Filter->IpList[Index])) {\r
+      IgmpJoinGroup (Private, &Filter->IpList[Index]);\r
+    }\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "\nIpFilter()  Exit #5  %Xh (%r)", StatCode, StatCode));\r
+\r
+  return StatCode;\r
+}\r
+\r
+\r
+/**\r
+  Call the IP filter\r
+\r
+  @param  Private              Pointer to Pxe BaseCode Protocol\r
+  @param  Filter               Pointer to the filter\r
+\r
+  @retval 0                    Successfully set the filter\r
+  @retval !0                   Failed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcIpFilter (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL  *This,\r
+  IN EFI_PXE_BASE_CODE_IP_FILTER *Filter\r
+  )\r
+{\r
+  EFI_STATUS          StatCode;\r
+  PXE_BASECODE_DEVICE *Private;\r
+  UINTN               Index;\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  for (Index = 0; Index < Filter->IpCnt; ++Index) {\r
+    if ((Filter->IpList[Index].Addr[0]) == BROADCAST_IPv4) {\r
+      //\r
+      // The  IP is a broadcast address.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Filter == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Issue BC command\r
+  //\r
+  StatCode = IpFilter (Private, Filter);\r
+\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+\r
+\r
+/**\r
+  Set the Base Code behavior parameters\r
+\r
+  @param  This                 Pointer to Pxe BaseCode Protocol\r
+  @param  AutoArpPtr           Boolean to do ARP stuff\r
+  @param  SendGuidPtr          Boolean whether or not to send GUID info\r
+  @param  TimeToLivePtr        Value for Total time to live\r
+  @param  TypeOfServicePtr     Value for Type of Service\r
+  @param  MakeCallbackPtr      Boolean to determine if we make callbacks\r
+\r
+  @retval 0                    Successfully set the parameters\r
+  @retval !0                   Failed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcSetParameters (\r
+  EFI_PXE_BASE_CODE_PROTOCOL  *This,\r
+  BOOLEAN                     *AutoArpPtr,\r
+  BOOLEAN                     *SendGuidPtr,\r
+  UINT8                       *TimeToLivePtr,\r
+  UINT8                       *TypeOfServicePtr,\r
+  BOOLEAN                     *MakeCallbackPtr\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxebcMode;\r
+  EFI_GUID                TmpGuid;\r
+  UINT8                   *SerialNumberPtr;\r
+  EFI_STATUS              StatCode;\r
+  PXE_BASECODE_DEVICE     *Private;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "\nSetParameters()  Entry.  "));\r
+\r
+  PxebcMode = Private->EfiBc.Mode;\r
+  StatCode  = EFI_SUCCESS;\r
+\r
+  if (SendGuidPtr != NULL) {\r
+    if (*SendGuidPtr) {\r
+      if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber (&TmpGuid, &SerialNumberPtr) != EFI_SUCCESS) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (MakeCallbackPtr != NULL) {\r
+    if (*MakeCallbackPtr) {\r
+      if (!SetMakeCallback (Private)) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+\r
+    PxebcMode->MakeCallbacks = *MakeCallbackPtr;\r
+  }\r
+\r
+  if (AutoArpPtr != NULL) {\r
+    PxebcMode->AutoArp = *AutoArpPtr;\r
+  }\r
+\r
+  if (SendGuidPtr != NULL) {\r
+    PxebcMode->SendGUID = *SendGuidPtr;\r
+  }\r
+\r
+  if (TimeToLivePtr != NULL) {\r
+    PxebcMode->TTL = *TimeToLivePtr;\r
+  }\r
+\r
+  if (TypeOfServicePtr != NULL) {\r
+    PxebcMode->ToS = *TypeOfServicePtr;\r
+  }\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  DEBUG ((DEBUG_INFO, "\nSetparameters()  Exit = %xh  ", StatCode));\r
+\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+//\r
+// //////////////////////////////////////////////////////////\r
+//\r
+//  BC Set Station IP Routine\r
+//\r
+\r
+/**\r
+  Set the station IP address\r
+\r
+  @param  This                 Pointer to Pxe BaseCode Protocol\r
+  @param  StationIpPtr         Pointer to the requested IP address to set in base\r
+                               code\r
+  @param  SubnetMaskPtr        Pointer to the requested subnet mask for the base\r
+                               code\r
+\r
+  @retval EFI_SUCCESS          Successfully set the parameters\r
+  @retval EFI_NOT_STARTED      BC has not started\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcSetStationIP (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+  IN EFI_IP_ADDRESS             *StationIpPtr,\r
+  IN EFI_IP_ADDRESS             *SubnetMaskPtr\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxebcMode;\r
+  EFI_STATUS              StatCode;\r
+  PXE_BASECODE_DEVICE     *Private;\r
+  UINT32                  SubnetMask;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    StatCode = EFI_NOT_STARTED;\r
+    goto RELEASE_LOCK;\r
+  }\r
+\r
+  PxebcMode = Private->EfiBc.Mode;\r
+\r
+  if (!Private->GoodStationIp && ((StationIpPtr == NULL) || (SubnetMaskPtr == NULL))) {\r
+    //\r
+    // It's not allowed to only set one of the two addresses while there isn't a previous\r
+    // GOOD address configuration.\r
+    //\r
+    StatCode = EFI_INVALID_PARAMETER;\r
+    goto RELEASE_LOCK;\r
+  }\r
+\r
+  if (SubnetMaskPtr != NULL) {\r
+    SubnetMask = SubnetMaskPtr->Addr[0];\r
+\r
+    if (SubnetMask & (SubnetMask + 1)) {\r
+      //\r
+      // the subnet mask is valid if it's with leading continuous 1 bits.\r
+      //\r
+      StatCode = EFI_INVALID_PARAMETER;\r
+      goto RELEASE_LOCK;\r
+    }\r
+  } else {\r
+    SubnetMaskPtr = &PxebcMode->SubnetMask;\r
+    SubnetMask    = SubnetMaskPtr->Addr[0];\r
+  }\r
+\r
+  if (StationIpPtr == NULL) {\r
+    StationIpPtr = &PxebcMode->StationIp;\r
+  }\r
+\r
+  if (!IS_INADDR_UNICAST (StationIpPtr) ||\r
+      ((StationIpPtr->Addr[0] | SubnetMask) == BROADCAST_IPv4)) {\r
+    //\r
+    // The station IP is not a unicast address.\r
+    //\r
+    StatCode = EFI_INVALID_PARAMETER;\r
+    goto RELEASE_LOCK;\r
+  }\r
+\r
+  PxebcMode->StationIp   = *StationIpPtr;\r
+  PxebcMode->SubnetMask  = *SubnetMaskPtr;\r
+  Private->GoodStationIp = TRUE;\r
+\r
+RELEASE_LOCK:\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  EfiReleaseLock (&Private->Lock);\r
+\r
+  return StatCode;\r
+}\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL mPxeBcDriverBinding = {\r
+  PxeBcDriverSupported,\r
+  PxeBcDriverStart,\r
+  PxeBcDriverStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+\r
+/**\r
+  Test to see if this driver supports Controller. Any Controller\r
+  than contains a Snp protocol can be supported.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  Controller           Handle of device to test.\r
+  @param  RemainingDevicePath  Not used.\r
+\r
+  @retval EFI_SUCCESS          This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on this device.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiSimpleNetworkProtocolGuid,\r
+                  (VOID **) &SnpPtr,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  gBS->CloseProtocol (\r
+        Controller,\r
+        &gEfiSimpleNetworkProtocolGuid,\r
+        This->DriverBindingHandle,\r
+        Controller\r
+        );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start the Base code driver.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  Controller           Handle of device to test.\r
+  @param  RemainingDevicePath  Not used.\r
+\r
+  @retval EFI_SUCCESS          This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on this device.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  PXE_BASECODE_DEVICE *Private;\r
+  LOADFILE_DEVICE     *pLF;\r
+\r
+  //\r
+  // Allocate structures needed by BaseCode and LoadFile protocols.\r
+  //\r
+  Private = AllocateZeroPool (sizeof (PXE_BASECODE_DEVICE));\r
+\r
+  if (Private == NULL ) {\r
+    DEBUG ((EFI_D_NET, "\nBcNotifySnp()  Could not alloc PXE_BASECODE_DEVICE structure.\n"));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  pLF = AllocateZeroPool (sizeof (LOADFILE_DEVICE));\r
+  if (pLF == NULL) {\r
+    DEBUG ((EFI_D_NET, "\nBcNotifySnp()  Could not alloc LOADFILE_DEVICE structure.\n"));\r
+    FreePool (Private);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Private->EfiBc.Mode = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_MODE));\r
+  if (Private->EfiBc.Mode == NULL) {\r
+    DEBUG ((EFI_D_NET, "\nBcNotifySnp()  Could not alloc Mode structure.\n"));\r
+    FreePool (Private);\r
+    FreePool (pLF);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Lock access, just in case\r
+  //\r
+  EfiInitializeLock (&Private->Lock, TPL_CALLBACK);\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  EfiInitializeLock (&pLF->Lock, TPL_CALLBACK);\r
+  EfiAcquireLock (&pLF->Lock);\r
+\r
+  //\r
+  // Initialize PXE structure\r
+  //\r
+  //\r
+  // First initialize the internal 'private' data that the application\r
+  // does not see.\r
+  //\r
+  Private->Signature  = PXE_BASECODE_DEVICE_SIGNATURE;\r
+  Private->Handle     = Controller;\r
+\r
+  //\r
+  // Get the NII interface\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
+                  (VOID **) &Private->NiiPtr,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Status = gBS->OpenProtocol (\r
+                    Controller,\r
+                    &gEfiNetworkInterfaceIdentifierProtocolGuid,\r
+                    (VOID **) &Private->NiiPtr,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto PxeBcError;\r
+    }\r
+  }\r
+  //\r
+  // Get the Snp interface\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiSimpleNetworkProtocolGuid,\r
+                  (VOID **) &Private->SimpleNetwork,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto PxeBcError;\r
+  }\r
+\r
+  //\r
+  // Next, initialize the external 'public' data that\r
+  // the application does see.\r
+  //\r
+  Private->EfiBc.Revision       = EFI_PXE_BASE_CODE_PROTOCOL_REVISION;\r
+  Private->EfiBc.Start          = BcStart;\r
+  Private->EfiBc.Stop           = BcStop;\r
+  Private->EfiBc.Dhcp           = BcDhcp;\r
+  Private->EfiBc.Discover       = BcDiscover;\r
+  Private->EfiBc.Mtftp          = BcMtftp;\r
+  Private->EfiBc.UdpWrite       = BcUdpWrite;\r
+  Private->EfiBc.UdpRead        = BcUdpRead;\r
+  Private->EfiBc.Arp            = BcArp;\r
+  Private->EfiBc.SetIpFilter    = BcIpFilter;\r
+  Private->EfiBc.SetParameters  = BcSetParameters;\r
+  Private->EfiBc.SetStationIp   = BcSetStationIP;\r
+  Private->EfiBc.SetPackets     = BcSetPackets;\r
+\r
+  //\r
+  // Initialize BaseCode Mode structure\r
+  //\r
+  Private->EfiBc.Mode->Started    = FALSE;\r
+  Private->EfiBc.Mode->TTL        = DEFAULT_TTL;\r
+  Private->EfiBc.Mode->ToS        = DEFAULT_ToS;\r
+  Private->EfiBc.Mode->UsingIpv6  = FALSE;\r
+  Private->EfiBc.Mode->AutoArp    = TRUE;\r
+\r
+  //\r
+  // Set to PXE_TRUE by the BC constructor if this BC\r
+  // implementation supports IPv6.\r
+  //\r
+  Private->EfiBc.Mode->Ipv6Supported = SUPPORT_IPV6;\r
+\r
+#if SUPPORT_IPV6\r
+  Private->EfiBc.Mode->Ipv6Available = Private->NiiPtr->Ipv6Supported;\r
+#else\r
+  Private->EfiBc.Mode->Ipv6Available = FALSE;\r
+#endif\r
+  //\r
+  // Set to TRUE by the BC constructor if this BC\r
+  // implementation supports BIS.\r
+  //\r
+  Private->EfiBc.Mode->BisSupported = TRUE;\r
+  Private->EfiBc.Mode->BisDetected  = PxebcBisDetect (Private);\r
+\r
+  //\r
+  // Initialize LoadFile structure.\r
+  //\r
+  pLF->Signature          = LOADFILE_DEVICE_SIGNATURE;\r
+  pLF->LoadFile.LoadFile  = LoadFile;\r
+  pLF->Private            = Private;\r
+\r
+  //\r
+  // Install protocol interfaces.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Controller,\r
+                  &gEfiPxeBaseCodeProtocolGuid,\r
+                  &Private->EfiBc,\r
+                  &gEfiLoadFileProtocolGuid,\r
+                  &pLF->LoadFile,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseProtocol (\r
+          Controller,\r
+          &gEfiSimpleNetworkProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Controller\r
+          );\r
+\r
+    goto PxeBcError;\r
+  }\r
+  //\r
+  // Release locks.\r
+  //\r
+  EfiReleaseLock (&pLF->Lock);\r
+  EfiReleaseLock (&Private->Lock);\r
+  return Status;\r
+\r
+PxeBcError: ;\r
+  gBS->FreePool (Private->EfiBc.Mode);\r
+  gBS->FreePool (Private);\r
+  gBS->FreePool (pLF);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop the Base code driver.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  Controller           Handle of device to test.\r
+  @param  NumberOfChildren     Not used\r
+  @param  ChildHandleBuffer    Not used\r
+\r
+  @retval EFI_SUCCESS          This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on this device.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN  EFI_HANDLE                     Controller,\r
+  IN  UINTN                          NumberOfChildren,\r
+  IN  EFI_HANDLE                     *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  EFI_LOAD_FILE_PROTOCOL  *LfProtocol;\r
+  LOADFILE_DEVICE         *LoadDevice;\r
+\r
+  //\r
+  // Get our context back.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiLoadFileProtocolGuid,\r
+                  (VOID **) &LfProtocol,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  LoadDevice = EFI_LOAD_FILE_DEV_FROM_THIS (LfProtocol);\r
+\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  Controller,\r
+                  &gEfiLoadFileProtocolGuid,\r
+                  &LoadDevice->LoadFile,\r
+                  &gEfiPxeBaseCodeProtocolGuid,\r
+                  &LoadDevice->Private->EfiBc,\r
+                  NULL\r
+                  );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+\r
+    Status = gBS->CloseProtocol (\r
+                    Controller,\r
+                    &gEfiSimpleNetworkProtocolGuid,\r
+                    This->DriverBindingHandle,\r
+                    Controller\r
+                    );\r
+\r
+    gBS->FreePool (LoadDevice->Private->EfiBc.Mode);\r
+    gBS->FreePool (LoadDevice->Private);\r
+    gBS->FreePool (LoadDevice);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Initialize the base code drivers and install the driver binding\r
+\r
+  Standard EFI Image Entry\r
+\r
+  @retval EFI_SUCCESS          This driver was successfully bound\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeBCDriver (\r
+  IN EFI_HANDLE       ImageHandle,\r
+  IN EFI_SYSTEM_TABLE *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // Initialize EFI library\r
+  //\r
+  Status = EfiLibInstallAllDriverProtocols (\r
+            ImageHandle,\r
+            SystemTable,\r
+            &mPxeBcDriverBinding,\r
+            NULL,\r
+            COMPONENT_NAME,\r
+            NULL,\r
+            NULL\r
+            );\r
+\r
+  InitArpHeader ();\r
+  OptionsStrucInit ();\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* eof - bc.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h
new file mode 100644 (file)
index 0000000..71d1518
--- /dev/null
@@ -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>\r
+\r
+#include <Guid/SmBios.h>\r
+#include <Protocol/Bis.h>\r
+#include <Protocol/PxeBaseCode.h>\r
+#include <Protocol/PxeBaseCodeCallBack.h>\r
+#include <Protocol/NetworkInterfaceIdentifier.h>\r
+#include <Protocol/SimpleNetwork.h>\r
+#include <Protocol/LoadFile.h>\r
+#include <Protocol/DevicePath.h>
+#include <Protocol/Tcp.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/BaseLib.h>\r
+#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 (file)
index 0000000..18dca38
--- /dev/null
@@ -0,0 +1,160 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  ComponentName.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Bc.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+EFI_COMPONENT_NAME_PROTOCOL     gPxeBcComponentName = {\r
+  PxeBcComponentNameGetDriverName,\r
+  PxeBcComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+static EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = {\r
+  {\r
+    "eng",\r
+    L"PXE Base Code Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the EFI Driver.\r
+\r
+  Arguments:\r
+    This       - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    Language   - A pointer to a three character ISO 639-2 language identifier.\r
+                 This is the language of the driver name that that the caller\r
+                 is requesting, and it must match one of the languages specified\r
+                 in SupportedLanguages.  The number of languages supported by a\r
+                 driver is up to the driver writer.\r
+    DriverName - A pointer to the Unicode string to return.  This Unicode string\r
+                 is the name of the driver specified by This in the language\r
+                 specified by Language.\r
+\r
+  Returns:\r
+    EFI_SUCCESS           - The Unicode string for the Driver specified by This\r
+                            and the language specified by Language was returned\r
+                            in DriverName.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - DriverName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return LookupUnicodeString (\r
+          Language,\r
+          gPxeBcComponentName.SupportedLanguages,\r
+          mPxeBcDriverNameTable,\r
+          DriverName\r
+          );\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the controller\r
+    that is being managed by an EFI Driver.\r
+\r
+  Arguments:\r
+    This             - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    ControllerHandle - The handle of a controller that the driver specified by\r
+                       This is managing.  This handle specifies the controller\r
+                       whose name is to be returned.\r
+    ChildHandle      - The handle of the child controller to retrieve the name\r
+                       of.  This is an optional parameter that may be NULL.  It\r
+                       will be NULL for device drivers.  It will also be NULL\r
+                       for a bus drivers that wish to retrieve the name of the\r
+                       bus controller.  It will not be NULL for a bus driver\r
+                       that wishes to retrieve the name of a child controller.\r
+    Language         - A pointer to a three character ISO 639-2 language\r
+                       identifier.  This is the language of the controller name\r
+                       that that the caller is requesting, and it must match one\r
+                       of the languages specified in SupportedLanguages.  The\r
+                       number of languages supported by a driver is up to the\r
+                       driver writer.\r
+    ControllerName   - A pointer to the Unicode string to return.  This Unicode\r
+                       string is the name of the controller specified by\r
+                       ControllerHandle and ChildHandle in the language specified\r
+                       by Language from the point of view of the driver specified\r
+                       by This.\r
+\r
+  Returns:\r
+    EFI_SUCCESS           - The Unicode string for the user readable name in the\r
+                            language specified by Language for the driver\r
+                            specified by This was returned in DriverName.\r
+    EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - ControllerName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This is not currently managing\r
+                            the controller specified by ControllerHandle and\r
+                            ChildHandle.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h
new file mode 100644 (file)
index 0000000..36f71f7
--- /dev/null
@@ -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 (file)
index 0000000..026dd24
--- /dev/null
@@ -0,0 +1,46 @@
+/** @file\r
+\r
+Copyright (c) 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeArch.c\r
+\r
+Abstract:\r
+  Defines PXE Arch type\r
+\r
+\r
+**/\r
+\r
+\r
+#include "PxeArch.h"\r
+\r
+UINT16 mSysArch = 0;\r
+\r
+UINT16\r
+GetSysArch (\r
+  VOID\r
+  )\r
+{\r
+  if (mSysArch == 0) {\r
+    //\r
+    // This is first call\r
+    // Assign to invalid value\r
+    //\r
+    mSysArch = 0xFFFF;\r
+\r
+    //\r
+    // We do not know what is EBC architecture.\r
+    // Maybe we can try to locate DebugSupport protocol to get ISA.\r
+    // TBD now.\r
+    //\r
+  }\r
+\r
+  return mSysArch;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h
new file mode 100644 (file)
index 0000000..0db856b
--- /dev/null
@@ -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 (file)
index 0000000..c4ace49
--- /dev/null
@@ -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 (file)
index 0000000..cc878d8
--- /dev/null
@@ -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 (file)
index 0000000..a482eb9
--- /dev/null
@@ -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 (file)
index 0000000..6d06045
--- /dev/null
@@ -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 (file)
index 0000000..f5eaa0f
--- /dev/null
@@ -0,0 +1,92 @@
+#/** @file\r
+# Component name for module BC\r
+#\r
+# Copyright (c) 2007, Intel Corporation\r
+#\r
+#  All rights reserved. This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution. The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = PxeBcDxe\r
+  FILE_GUID                      = A3f436EA-A127-4EF8-957C-8048606FF670\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  EDK_RELEASE_VERSION            = 0x00020000\r
+  EFI_SPECIFICATION_VERSION      = 0x00020000\r
+\r
+  ENTRY_POINT                    = InitializeBCDriver\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources.common]\r
+  Pxe_bc_mtftp.c\r
+  Bc.c\r
+  Dhcp.h\r
+  Ip.h\r
+  Pxe_bc_ip.c\r
+  Pxe_bc_dhcp.c\r
+  Pxe_bc_arp.c\r
+  Hton.h\r
+  ComponentName.c\r
+  Bc.h\r
+  Pxe_loadfile.c\r
+  Tftp.h\r
+  Pxe_bc_igmp.c\r
+  Pxe_bc_udp.c\r
+\r
+[Sources.IA32]\r
+  Ia32\PxeArch.h\r
+\r
+[Sources.X64]\r
+  X64\PxeArch.h\r
+\r
+[Sources.IPF]\r
+  Ipf\PxeArch.h\r
+\r
+[Sources.EBC]\r
+  Ebc\PxeArch.h\r
+  Ebc\PxeArch.c\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  UefiLib\r
+  BaseLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  BaseMemoryLib\r
+  DebugLib\r
+\r
+\r
+[Guids]\r
+  gEfiSmbiosTableGuid                           # ALWAYS_CONSUMED\r
+\r
+\r
+[Protocols]\r
+  gEfiBisProtocolGuid                             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiPxeBaseCodeCallbackProtocolGuid             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiPxeBaseCodeProtocolGuid                     # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiLoadFileProtocolGuid                        # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiSimpleNetworkProtocolGuid                   # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiDevicePathProtocolGuid                      # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiNetworkInterfaceIdentifierProtocolGuid      # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiTcpProtocolGuid                             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiNetworkInterfaceIdentifierProtocolGuid_31   # PROTOCOL ALWAYS_CONSUMED\r
+\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa
new file mode 100644 (file)
index 0000000..1a1f81d
--- /dev/null
@@ -0,0 +1,106 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+  <MsaHeader>\r
+    <ModuleName>BC</ModuleName>\r
+    <ModuleType>DXE_DRIVER</ModuleType>\r
+    <GuidValue>A3f436EA-A127-4EF8-957C-8048606FF670</GuidValue>\r
+    <Version>1.0</Version>\r
+    <Abstract>Component name for module BC</Abstract>\r
+    <Description>FIX ME!</Description>\r
+    <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>\r
+    <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>\r
+    <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION   0x00000052</Specification>\r
+  </MsaHeader>\r
+  <ModuleDefinitions>\r
+    <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>\r
+    <BinaryModule>false</BinaryModule>\r
+    <OutputFileBasename>BC</OutputFileBasename>\r
+  </ModuleDefinitions>\r
+  <LibraryClassDefinitions>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>DebugLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>BaseMemoryLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiDriverEntryPoint</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiBootServicesTableLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>BaseLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiLib</Keyword>\r
+    </LibraryClass>\r
+  </LibraryClassDefinitions>\r
+  <SourceFiles>\r
+    <Filename>pxe_bc_udp.c</Filename>\r
+    <Filename>pxe_bc_igmp.c</Filename>\r
+    <Filename>tftp.h</Filename>\r
+    <Filename>pxe_loadfile.c</Filename>\r
+    <Filename>bc.h</Filename>\r
+    <Filename>ComponentName.c</Filename>\r
+    <Filename>BcEntry.c</Filename>\r
+    <Filename>ipf\PxeArch.h</Filename>\r
+    <Filename>ebc\PxeArch.h</Filename>\r
+    <Filename SupArchList="X64">x64\PxeArch.h</Filename>\r
+    <Filename>pxe_bc_tcp.c</Filename>\r
+    <Filename>hton.h</Filename>\r
+    <Filename>pxe_bc_arp.c</Filename>\r
+    <Filename>pxe_bc_dhcp.c</Filename>\r
+    <Filename>pxe_bc_ip.c</Filename>\r
+    <Filename>ip.h</Filename>\r
+    <Filename>dhcp.h</Filename>\r
+    <Filename>bc.c</Filename>\r
+    <Filename>pxe_bc_mtftp.c</Filename>\r
+    <Filename>ia32\PxeArch.h</Filename>\r
+    <Filename>ebc\PxeArch.c</Filename>\r
+  </SourceFiles>\r
+  <PackageDependencies>\r
+    <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>\r
+    <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>\r
+  </PackageDependencies>\r
+  <Protocols>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiNetworkInterfaceIdentifierProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiDevicePathProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiSimpleNetworkProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiLoadFileProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiPxeBaseCodeProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiPxeBaseCodeCallbackProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiBisProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+  </Protocols>\r
+  <Guids>\r
+    <GuidCNames Usage="ALWAYS_CONSUMED">\r
+      <GuidCName>gEfiSmbiosTableGuid</GuidCName>\r
+    </GuidCNames>\r
+  </Guids>\r
+  <Externs>\r
+    <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>\r
+    <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>\r
+    <Extern>\r
+      <ModuleEntryPoint>InitializeBCDriver</ModuleEntryPoint>\r
+    </Extern>\r
+  </Externs>\r
+</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 (file)
index 0000000..3654363
--- /dev/null
@@ -0,0 +1,583 @@
+/** @file\r
+\r
+Copyright (c) 2004, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  pxe_bc_arp.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Bc.h"\r
+\r
+//\r
+// Definitions for ARP\r
+// Per RFC 826\r
+//\r
+STATIC ARP_HEADER ArpHeader;\r
+\r
+#pragma pack(1)\r
+STATIC struct {\r
+  UINT8       MediaHeader[14];\r
+  ARP_HEADER  ArpHeader;\r
+  UINT8       ArpData[64];\r
+} ArpReplyPacket;\r
+#pragma pack()\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return none\r
+\r
+**/\r
+VOID\r
+InitArpHeader (\r
+  VOID\r
+  )\r
+{\r
+  ArpHeader.HwType      = HTONS (ETHERNET_ADD_SPC);\r
+  ArpHeader.ProtType    = HTONS (ETHER_TYPE_IP);\r
+  ArpHeader.HwAddLen    = ENET_HWADDLEN;\r
+  ArpHeader.ProtAddLen  = IPV4_PROTADDLEN;\r
+  ArpHeader.OpCode      = HTONS (ARP_REQUEST);\r
+\r
+  CopyMem (&ArpReplyPacket.ArpHeader, &ArpHeader, sizeof (ARP_HEADER));\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+VOID\r
+HandleArpReceive (\r
+  IN PXE_BASECODE_DEVICE  *Private,\r
+  IN ARP_PACKET           *ArpPacketPtr,\r
+  IN VOID                 *MediaHeader\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  EFI_SIMPLE_NETWORK_MODE *SnpMode;\r
+  EFI_MAC_ADDRESS         TmpMacAddr;\r
+  UINTN                   Index;\r
+  UINT8                   *SrcHwAddr;\r
+  UINT8                   *SrcPrAddr;\r
+  UINT8                   *DstHwAddr;\r
+  UINT8                   *DstPrAddr;\r
+  UINT8                   *TmpPtr;\r
+\r
+  //\r
+  //\r
+  //\r
+  PxeBcMode = Private->EfiBc.Mode;\r
+  SnpMode   = Private->SimpleNetwork->Mode;\r
+\r
+  //\r
+  // For now only ethernet addresses are supported.\r
+  // This will need to be updated when other media\r
+  // layers are supported by PxeBc, Snp and UNDI.\r
+  //\r
+  if (ArpPacketPtr->ArpHeader.HwType != HTONS (ETHERNET_ADD_SPC)) {\r
+    return ;\r
+  }\r
+  //\r
+  // For now only IP protocol addresses are supported.\r
+  // This will need to be updated when other protocol\r
+  // types are supported by PxeBc, Snp and UNDI.\r
+  //\r
+  if (ArpPacketPtr->ArpHeader.ProtType != HTONS (ETHER_TYPE_IP)) {\r
+    return ;\r
+  }\r
+  //\r
+  // For now only SNP hardware address sizes are supported.\r
+  //\r
+  if (ArpPacketPtr->ArpHeader.HwAddLen != SnpMode->HwAddressSize) {\r
+    return ;\r
+  }\r
+  //\r
+  // For now only PxeBc protocol address sizes are supported.\r
+  //\r
+  if (ArpPacketPtr->ArpHeader.ProtAddLen != Private->IpLength) {\r
+    return ;\r
+  }\r
+  //\r
+  // Ignore out of range opcodes\r
+  //\r
+  switch (ArpPacketPtr->ArpHeader.OpCode) {\r
+  case HTONS (ARP_REPLY):\r
+  case HTONS (ARP_REQUEST):\r
+    break;\r
+\r
+  default:\r
+    return ;\r
+  }\r
+  //\r
+  // update entry in our ARP cache if we have it\r
+  //\r
+  SrcHwAddr = (UINT8 *) &ArpPacketPtr->SrcHardwareAddr;\r
+  SrcPrAddr = SrcHwAddr + SnpMode->HwAddressSize;\r
+\r
+  for (Index = 0; Index < PxeBcMode->ArpCacheEntries; ++Index) {\r
+    if (CompareMem (\r
+          &PxeBcMode->ArpCache[Index].IpAddr,\r
+          SrcPrAddr,\r
+          Private->IpLength\r
+          )) {\r
+      continue;\r
+    }\r
+\r
+    CopyMem (\r
+      &PxeBcMode->ArpCache[Index].MacAddr,\r
+      SrcHwAddr,\r
+      SnpMode->HwAddressSize\r
+      );\r
+\r
+    break;\r
+  }\r
+  //\r
+  // Done if ARP packet was not for us.\r
+  //\r
+  DstHwAddr = SrcPrAddr + Private->IpLength;\r
+  DstPrAddr = DstHwAddr + SnpMode->HwAddressSize;\r
+\r
+  if (CompareMem (DstPrAddr, &PxeBcMode->StationIp, Private->IpLength)) {\r
+    return ;\r
+    //\r
+    // not for us\r
+    //\r
+  }\r
+  //\r
+  // for us - if we did not update entry, add it\r
+  //\r
+  if (Index == PxeBcMode->ArpCacheEntries) {\r
+    //\r
+    // if we have a full table, get rid of oldest\r
+    //\r
+    if (Index == PXE_ARP_CACHE_SIZE) {\r
+      Index = Private->OldestArpEntry;\r
+\r
+      if (++Private->OldestArpEntry == PXE_ARP_CACHE_SIZE) {\r
+        Private->OldestArpEntry = 0;\r
+      }\r
+    } else {\r
+      ++PxeBcMode->ArpCacheEntries;\r
+    }\r
+\r
+    CopyMem (\r
+      &PxeBcMode->ArpCache[Index].MacAddr,\r
+      SrcHwAddr,\r
+      SnpMode->HwAddressSize\r
+      );\r
+\r
+    CopyMem (\r
+      &PxeBcMode->ArpCache[Index].IpAddr,\r
+      SrcPrAddr,\r
+      Private->IpLength\r
+      );\r
+  }\r
+  //\r
+  // if this is not a request or we don't yet have an IP, finished\r
+  //\r
+  if (ArpPacketPtr->ArpHeader.OpCode != HTONS (ARP_REQUEST) || !Private->GoodStationIp) {\r
+    return ;\r
+  }\r
+  //\r
+  // Assemble ARP reply.\r
+  //\r
+  //\r
+  // Create media header.  [ dest mac | src mac | prot ]\r
+  //\r
+  CopyMem (\r
+    &ArpReplyPacket.MediaHeader[0],\r
+    SrcHwAddr,\r
+    SnpMode->HwAddressSize\r
+    );\r
+\r
+  CopyMem (\r
+    &ArpReplyPacket.MediaHeader[SnpMode->HwAddressSize],\r
+    &SnpMode->CurrentAddress,\r
+    SnpMode->HwAddressSize\r
+    );\r
+\r
+  CopyMem (\r
+    &ArpReplyPacket.MediaHeader[2 * SnpMode->HwAddressSize],\r
+    &((UINT8 *) MediaHeader)[2 * SnpMode->HwAddressSize],\r
+    sizeof (UINT16)\r
+    );\r
+\r
+  //\r
+  // ARP reply header is almost filled in,\r
+  // just insert the correct opcode.\r
+  //\r
+  ArpReplyPacket.ArpHeader.OpCode = HTONS (ARP_REPLY);\r
+\r
+  //\r
+  // Now fill in ARP data.  [ src mac | src prot | dest mac | dest prot ]\r
+  //\r
+  TmpPtr = ArpReplyPacket.ArpData;\r
+  CopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize);\r
+\r
+  TmpPtr += SnpMode->HwAddressSize;\r
+  CopyMem (TmpPtr, &PxeBcMode->StationIp, Private->IpLength);\r
+\r
+  TmpPtr += Private->IpLength;\r
+  CopyMem (TmpPtr, SrcHwAddr, SnpMode->HwAddressSize);\r
+\r
+  TmpPtr += SnpMode->HwAddressSize;\r
+  CopyMem (TmpPtr, SrcPrAddr, Private->IpLength);\r
+\r
+  //\r
+  // Now send out the ARP reply.\r
+  //\r
+  CopyMem (&TmpMacAddr, SrcHwAddr, sizeof (EFI_MAC_ADDRESS));\r
+\r
+  SendPacket (\r
+    Private,\r
+    &ArpReplyPacket.MediaHeader,\r
+    &ArpReplyPacket.ArpHeader,\r
+    sizeof (ARP_HEADER) + 2 * (Private->IpLength + SnpMode->HwAddressSize),\r
+    &TmpMacAddr,\r
+    PXE_PROTOCOL_ETHERNET_ARP,\r
+    EFI_PXE_BASE_CODE_FUNCTION_ARP\r
+    );\r
+\r
+  //\r
+  // Give time (100 microseconds) for ARP reply to get onto wire.\r
+  //\r
+  gBS->Stall (1000);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return TRUE := If IP address was found and MAC address was stored\r
+  @return FALSE := If IP address was not found\r
+\r
+**/\r
+BOOLEAN\r
+GetHwAddr (\r
+  IN PXE_BASECODE_DEVICE  *Private,\r
+  IN EFI_IP_ADDRESS       *ProtocolAddrPtr,\r
+  OUT EFI_MAC_ADDRESS     *HardwareAddrPtr\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  UINTN                   HardwareAddrLength;\r
+  UINTN                   Index;\r
+\r
+  PxeBcMode           = Private->EfiBc.Mode;\r
+  HardwareAddrLength  = Private->SimpleNetwork->Mode->HwAddressSize;\r
+\r
+  for (Index = 0; Index < PxeBcMode->ArpCacheEntries; ++Index) {\r
+    if (!CompareMem (\r
+          ProtocolAddrPtr,\r
+          &PxeBcMode->ArpCache[Index].IpAddr,\r
+          Private->IpLength\r
+          )) {\r
+      CopyMem (\r
+        HardwareAddrPtr,\r
+        &PxeBcMode->ArpCache[Index].MacAddr,\r
+        HardwareAddrLength\r
+        );\r
+\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS := ARP request sent\r
+  @return other := ARP request could not be sent\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SendRequest (\r
+  IN PXE_BASECODE_DEVICE  *Private,\r
+  IN EFI_IP_ADDRESS       *ProtocolAddrPtr,\r
+  IN EFI_MAC_ADDRESS      *HardwareAddrPtr\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  EFI_SIMPLE_NETWORK_MODE *SnpMode;\r
+  ARP_PACKET              *ArpPacket;\r
+  EFI_STATUS              Status;\r
+  UINTN                   HardwareAddrLength;\r
+  UINT8                   *SrcProtocolAddrPtr;\r
+  UINT8                   *DestHardwareAddrptr;\r
+  UINT8                   *DestProtocolAddrPtr;\r
+\r
+  //\r
+  //\r
+  //\r
+  PxeBcMode           = Private->EfiBc.Mode;\r
+  SnpMode             = Private->SimpleNetwork->Mode;\r
+  HardwareAddrLength  = SnpMode->HwAddressSize;\r
+\r
+  //\r
+  // Allocate ARP buffer\r
+  //\r
+  if (Private->ArpBuffer == NULL) {\r
+    Status = gBS->AllocatePool (\r
+                    EfiBootServicesData,\r
+                    SnpMode->MediaHeaderSize + sizeof (ARP_PACKET),\r
+                    &Private->ArpBuffer\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  ArpPacket = (VOID *) (Private->ArpBuffer + SnpMode->MediaHeaderSize);\r
+\r
+  //\r
+  // for now, only handle one kind of hw and pr address\r
+  //\r
+  ArpPacket->ArpHeader            = ArpHeader;\r
+  ArpPacket->ArpHeader.HwAddLen   = (UINT8) HardwareAddrLength;\r
+  ArpPacket->ArpHeader.ProtAddLen = (UINT8) Private->IpLength;\r
+\r
+  //\r
+  // rest more generic\r
+  //\r
+  SrcProtocolAddrPtr  = (UINT8 *) (&ArpPacket->SrcHardwareAddr) + HardwareAddrLength;\r
+  DestHardwareAddrptr = SrcProtocolAddrPtr + Private->IpLength;\r
+  DestProtocolAddrPtr = DestHardwareAddrptr + HardwareAddrLength;\r
+\r
+  CopyMem (DestProtocolAddrPtr, ProtocolAddrPtr, Private->IpLength);\r
+  CopyMem (DestHardwareAddrptr, HardwareAddrPtr, HardwareAddrLength);\r
+  CopyMem (SrcProtocolAddrPtr, &PxeBcMode->StationIp, Private->IpLength);\r
+  CopyMem (\r
+    &ArpPacket->SrcHardwareAddr,\r
+    &SnpMode->CurrentAddress,\r
+    HardwareAddrLength\r
+    );\r
+\r
+  return SendPacket (\r
+          Private,\r
+          Private->ArpBuffer,\r
+          ArpPacket,\r
+          sizeof (ARP_HEADER) + ((Private->IpLength + HardwareAddrLength) << 1),\r
+          &SnpMode->BroadcastAddress,\r
+          PXE_PROTOCOL_ETHERNET_ARP,\r
+          EFI_PXE_BASE_CODE_FUNCTION_ARP\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// check for address - if not there, send ARP request, wait and check again\r
+// not how it would be done in a full system\r
+//\r
+#define ARP_REQUEST_TIMEOUT_MS  500 // try for half a second\r
+\r
+  ////////////////////////////////////////////////////////////\r
+//\r
+//  BC Arp Routine\r
+//\r
+\r
+/**\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcArp (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL * This,\r
+  IN EFI_IP_ADDRESS             * ProtocolAddrPtr,\r
+  OUT EFI_MAC_ADDRESS           * HardwareAddrPtr OPTIONAL\r
+  )\r
+{\r
+  EFI_MAC_ADDRESS     Mac;\r
+  EFI_STATUS          StatCode;\r
+  PXE_BASECODE_DEVICE *Private;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "\nBcArp()"));\r
+\r
+  //\r
+  // Issue BC command\r
+  //\r
+  if (ProtocolAddrPtr == NULL) {\r
+    DEBUG (\r
+      (DEBUG_INFO,\r
+      "\nBcArp()  Exit #1  %Xh (%r)",\r
+      EFI_INVALID_PARAMETER,\r
+      EFI_INVALID_PARAMETER)\r
+      );\r
+\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (HardwareAddrPtr == NULL) {\r
+    HardwareAddrPtr = &Mac;\r
+  }\r
+\r
+  ZeroMem (HardwareAddrPtr, Private->SimpleNetwork->Mode->HwAddressSize);\r
+\r
+  if (GetHwAddr (Private, ProtocolAddrPtr, HardwareAddrPtr)) {\r
+    DEBUG (\r
+      (DEBUG_INFO,\r
+      "\nBcArp()  Exit #2  %Xh (%r)",\r
+      EFI_SUCCESS,\r
+      EFI_SUCCESS)\r
+      );\r
+\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  StatCode = DoArp (Private, ProtocolAddrPtr, HardwareAddrPtr);\r
+\r
+  DEBUG ((DEBUG_INFO, "\nBcArp()  Exit #3  %Xh (%r)", StatCode, StatCode));\r
+\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS := MAC address found\r
+  @return other := MAC address could not be found\r
+\r
+**/\r
+EFI_STATUS\r
+DoArp (\r
+  IN PXE_BASECODE_DEVICE  *Private,\r
+  IN EFI_IP_ADDRESS       *ProtocolAddrPtr,\r
+  OUT EFI_MAC_ADDRESS     *HardwareAddrPtr\r
+  )\r
+{\r
+  EFI_STATUS  StatCode;\r
+  EFI_EVENT   TimeoutEvent;\r
+  UINTN       HeaderSize;\r
+  UINTN       BufferSize;\r
+  UINT16      Protocol;\r
+\r
+  DEBUG ((DEBUG_INFO, "\nDoArp()"));\r
+\r
+  //\r
+  //\r
+  //\r
+  StatCode = SendRequest (Private, ProtocolAddrPtr, HardwareAddrPtr);\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    DEBUG ((DEBUG_INFO, "\nDoArp()  Exit #1  %Xh (%r)", StatCode, StatCode));\r
+    return StatCode;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  StatCode = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &TimeoutEvent\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    return StatCode;\r
+  }\r
+\r
+  StatCode = gBS->SetTimer (\r
+                    TimeoutEvent,\r
+                    TimerRelative,\r
+                    ARP_REQUEST_TIMEOUT_MS * 10000\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return StatCode;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  for (;;) {\r
+    StatCode = WaitForReceive (\r
+                Private,\r
+                EFI_PXE_BASE_CODE_FUNCTION_ARP,\r
+                TimeoutEvent,\r
+                &HeaderSize,\r
+                &BufferSize,\r
+                &Protocol\r
+                );\r
+\r
+    if (EFI_ERROR (StatCode)) {\r
+      break;\r
+    }\r
+\r
+    if (Protocol != PXE_PROTOCOL_ETHERNET_ARP) {\r
+      continue;\r
+    }\r
+\r
+    HandleArpReceive (\r
+      Private,\r
+      (ARP_PACKET *) (Private->ReceiveBufferPtr + HeaderSize),\r
+      Private->ReceiveBufferPtr\r
+      );\r
+\r
+    if (GetHwAddr (Private, ProtocolAddrPtr, HardwareAddrPtr)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nDoArp()  Exit #2  %Xh, (%r)",\r
+    StatCode,\r
+    StatCode)\r
+    );\r
+\r
+  gBS->CloseEvent (TimeoutEvent);\r
+\r
+  return StatCode;\r
+}\r
+\r
+/* eof - pxe_bc_arp.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c
new file mode 100644 (file)
index 0000000..7dec5ec
--- /dev/null
@@ -0,0 +1,3284 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  pxe_bc_dhcp.c\r
+\r
+Abstract:\r
+  DHCP and PXE discovery protocol implementations.\r
+\r
+\r
+**/\r
+\r
+#include "Bc.h"\r
+\r
+#include "PxeArch.h"\r
+\r
+STATIC EFI_PXE_BASE_CODE_UDP_PORT DhcpServerPort        = DHCP_SERVER_PORT;\r
+STATIC EFI_PXE_BASE_CODE_UDP_PORT DHCPClientPort        = DHCP_CLIENT_PORT;\r
+STATIC EFI_PXE_BASE_CODE_UDP_PORT PseudoDhcpServerPort  = PXE_DISCOVERY_PORT;\r
+#define PSEUDO_DHCP_CLIENT_PORT PseudoDhcpServerPort\r
+STATIC EFI_IP_ADDRESS             BroadcastIP       = { 0xffffffff };\r
+STATIC EFI_IP_ADDRESS             DefaultSubnetMask = { 0xffffff00 };\r
+\r
+typedef union {\r
+  DHCPV4_OP_STRUCT          *OpPtr;\r
+  PXE_OP_SERVER_LIST        *BootServersStr;\r
+  PXE_SERVER_LIST           *BootServerList;\r
+  PXE_BOOT_MENU_ENTRY       *BootMenuItem;\r
+  PXE_OP_DISCOVERY_CONTROL  *DiscoveryControl;\r
+  PXE_OP_BOOT_MENU          *BootMenu;\r
+  PXE_OP_BOOT_ITEM          *BootItem;\r
+  DHCPV4_OP_VENDOR_OPTIONS  *VendorOptions;\r
+  DHCPV4_OP_OVERLOAD        *Overload;\r
+  DHCPV4_OP_CLASS           *PxeClassStr;\r
+  DHCPV4_OP_SUBNET_MASK     *SubnetMaskStr;\r
+  DHCPV4_OP_MESSAGE_TYPE    *MessageType;\r
+  UINT8                     *BytePtr;\r
+} UNION_PTR;\r
+\r
+#pragma pack(1)\r
+//\r
+// option structure for DHCPREQUEST at end of DISCOVER options\r
+// and for DHCPDECLINE\r
+//\r
+STATIC const struct requestopendstr {\r
+  DHCPV4_OP_REQUESTED_IP  OpReqIP;\r
+  DHCPV4_OP_SERVER_IP     DhcServerIpPtr;\r
+  UINT8                   End[1];\r
+}\r
+RequestOpEndStr = {\r
+  {\r
+    {\r
+      OP_DHCP_REQ_IP_ADD,\r
+      DHCPV4_OPTION_LENGTH(DHCPV4_OP_REQUESTED_IP)\r
+    }\r
+  },\r
+  {\r
+    {\r
+      OP_DHCP_SERVER_IP,\r
+      DHCPV4_OPTION_LENGTH(DHCPV4_OP_SERVER_IP)\r
+    }\r
+  },\r
+  {\r
+    OP_END\r
+  }\r
+};\r
+\r
+#define DHCP_REQ_OPTIONS  (*(struct requestopendstr *) DHCPV4_OPTIONS_BUFFER.End)\r
+\r
+PXE_OP_BOOT_ITEM                DefaultBootItem = {\r
+  {\r
+    VEND_PXE_BOOT_ITEM,\r
+    DHCPV4_OPTION_LENGTH(PXE_OP_BOOT_ITEM)\r
+  },\r
+  0,\r
+  0\r
+};\r
+\r
+//\r
+// PXE discovery control default structure\r
+//\r
+STATIC PXE_OP_DISCOVERY_CONTROL DefaultDisCtl = {\r
+  { VEND_PXE_DISCOVERY_CONTROL, DHCPV4_OPTION_LENGTH(PXE_OP_DISCOVERY_CONTROL) },\r
+  0\r
+};\r
+\r
+//\r
+// PXE credentials option structure\r
+//\r
+typedef struct {\r
+  UINT8 c[4];\r
+} PXE_CREDENTIAL;\r
+\r
+typedef struct {\r
+  DHCPV4_OP_HEADER  Header;\r
+  PXE_CREDENTIAL    Credentials[1];\r
+} PXE_OP_CREDENTIAL_TYPES;\r
+\r
+//\r
+// option structure for PXE discover (without credentials)\r
+//\r
+typedef struct {            // discoveropendstr {\r
+  DHCPV4_OP_HEADER  Header; // vendor options\r
+  PXE_OP_BOOT_ITEM  BootItem;\r
+  UINT8             End[1]; // if credentials option, it starts here\r
+} PXE_DISCOVER_OPTIONS;\r
+\r
+#define DISCOVERoptions (*(PXE_DISCOVER_OPTIONS *) DHCPV4_OPTIONS_BUFFER.End)\r
+#define DISCREDoptions  (*(PXE_OP_CREDENTIAL_TYPES *) DISCOVERoptions.End)\r
+\r
+//\r
+// common option beginning for all our DHCP messages except\r
+// DHCPDECLINE and DHCPRELEASE\r
+//\r
+STATIC struct optionsstr {\r
+  UINT8                       DhcpCookie[4];\r
+  DHCPV4_OP_MESSAGE_TYPE      DhcpMessageType;\r
+  DHCPV4_OP_MAX_MESSAGE_SIZE  DhcpMaxMessageSize;\r
+  DHCPV4_OP_REQUESTED_OPTIONS DhcpRequestedOptions;\r
+  DHCPV4_OP_PLATFORM_ID       DhcpPlatformId;\r
+  DHCPV4_OP_NETWORK_INTERFACE DhcpNetworkInterface;\r
+  DHCPV4_OP_ARCHITECTURE_TYPE DhcpClientArchitecture;\r
+  DHCPV4_OP_CLASS_ID          DhcpClassIdentifier;\r
+  UINT8                       End[1];\r
+} DHCPOpStart;\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+OptionsStrucInit (\r
+  VOID\r
+  )\r
+{\r
+  DHCPOpStart.DhcpCookie[0] = 99;\r
+  DHCPOpStart.DhcpCookie[1] = 130;\r
+  DHCPOpStart.DhcpCookie[2] = 83;\r
+  DHCPOpStart.DhcpCookie[3] = 99;\r
+  DHCPOpStart.DhcpMessageType.Header.OpCode = OP_DHCP_MESSAGE_TYPE;\r
+  DHCPOpStart.DhcpMessageType.Header.Length = 1;\r
+  DHCPOpStart.DhcpMessageType.Type = DHCPDISCOVER;\r
+  DHCPOpStart.DhcpMaxMessageSize.Header.OpCode = OP_DHCP_MAX_MESSAGE_SZ;\r
+  DHCPOpStart.DhcpMaxMessageSize.Header.Length = 2;\r
+  DHCPOpStart.DhcpMaxMessageSize.MaxSize[0] = MAX_DHCP_MSG_SZ >> 8;\r
+  DHCPOpStart.DhcpMaxMessageSize.MaxSize[1] = MAX_DHCP_MSG_SZ & 0xff;\r
+  DHCPOpStart.DhcpRequestedOptions.Header.OpCode = OP_DHCP_PARM_REQ_LIST;\r
+  DHCPOpStart.DhcpRequestedOptions.Header.Length = sizeof (DHCPV4_REQUESTED_OPTIONS_DATA);\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_SUBNET_MASK = OP_SUBNET_MASK;                     /* 1 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_OFFSET = OP_TIME_OFFSET;                     /* 2 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_ROUTER_LIST = OP_ROUTER_LIST;                     /* 3 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_SERVERS = OP_TIME_SERVERS;                   /* 4 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_NAME_SERVERS = OP_NAME_SERVERS;                   /* 5 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DNS_SERVERS = OP_DNS_SERVERS;                     /* 6 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_HOST_NAME = OP_HOST_NAME;                         /* 12 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_BOOT_FILE_SZ = OP_BOOT_FILE_SZ;                   /* 13 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DOMAIN_NAME = OP_DOMAIN_NAME;                     /* 15 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_ROOT_PATH = OP_ROOT_PATH;                         /* 17 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_EXTENSION_PATH = OP_EXTENSION_PATH;               /* 18 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_MAX_DATAGRAM_SZ = OP_MAX_DATAGRAM_SZ;             /* 22 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DEFAULT_TTL = OP_DEFAULT_TTL;                     /* 23 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_BROADCAST_ADD = OP_BROADCAST_ADD;                 /* 28 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_DOMAIN_NAME = OP_NIS_DOMAIN_NAME;             /* 40 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_SERVERS = OP_NIS_SERVERS;                     /* 41 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_NTP_SERVERS = OP_NTP_SERVERS;                     /* 42 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_VENDOR_SPECIFIC = OP_VENDOR_SPECIFIC;             /* 43 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REQ_IP_ADD = OP_DHCP_REQ_IP_ADD;             /* 50 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_LEASE_TIME = OP_DHCP_LEASE_TIME;             /* 51 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_SERVER_IP = OP_DHCP_SERVER_IP;               /* 54 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_RENEWAL_TIME = OP_DHCP_RENEWAL_TIME;         /* 58 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REBINDING_TIME = OP_DHCP_REBINDING_TIME;     /* 59 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_CLASS_IDENTIFIER = OP_DHCP_CLASS_IDENTIFIER; /* 60 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_TFTP_SERVER_NAME = OP_DHCP_TFTP_SERVER_NAME; /* 66 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_BOOTFILE = OP_DHCP_BOOTFILE;                 /* 67 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_PLATFORM_ID = OP_DHCP_PLATFORM_ID;           /* 97 */\r
+  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption128 = 128;\r
+  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption129 = 129;\r
+  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption130 = 130;\r
+  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption131 = 131;\r
+  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption132 = 132;\r
+  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption133 = 133, DHCPOpStart.DhcpRequestedOptions.Data.VendorOption134 = 134;\r
+  DHCPOpStart.DhcpRequestedOptions.Data.VendorOption135 = 135;\r
+  DHCPOpStart.DhcpPlatformId.Header.OpCode              = OP_DHCP_PLATFORM_ID;\r
+  DHCPOpStart.DhcpPlatformId.Header.Length              = DHCPV4_OPTION_LENGTH (DHCPV4_OP_PLATFORM_ID);\r
+  DHCPOpStart.DhcpNetworkInterface.Header.OpCode        = OP_DHCP_NETWORK_ARCH;\r
+  DHCPOpStart.DhcpNetworkInterface.Header.Length        = DHCPV4_OPTION_LENGTH (DHCPV4_OP_NETWORK_INTERFACE);\r
+  DHCPOpStart.DhcpNetworkInterface.Type                 = 0;\r
+  DHCPOpStart.DhcpNetworkInterface.MajorVersion         = 0;\r
+  DHCPOpStart.DhcpNetworkInterface.MinorVersion         = 0;\r
+  DHCPOpStart.DhcpClientArchitecture.Header.OpCode      = OP_DHCP_SYSTEM_ARCH;\r
+  DHCPOpStart.DhcpClientArchitecture.Header.Length      = DHCPV4_OPTION_LENGTH (DHCPV4_OP_ARCHITECTURE_TYPE);\r
+  DHCPOpStart.DhcpClientArchitecture.Type               = HTONS (SYS_ARCH);\r
+  DHCPOpStart.DhcpClassIdentifier.Header.OpCode         = OP_DHCP_CLASS_IDENTIFIER;\r
+  DHCPOpStart.DhcpClassIdentifier.Header.Length         = sizeof (DHCPV4_CLASS_ID_DATA);\r
+  CopyMem (\r
+    DHCPOpStart.DhcpClassIdentifier.Data.ClassIdentifier,\r
+    "PXEClient:",\r
+    sizeof ("PXEClient:")\r
+    );\r
+  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit2, "Arch:", sizeof ("Arch:"));\r
+  CopyMem (\r
+    DHCPOpStart.DhcpClassIdentifier.Data.ArchitectureType,\r
+    "xxxxx",\r
+    sizeof ("xxxxx")\r
+    );\r
+  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit3, ":", sizeof (":"));\r
+  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.InterfaceName, "XXXX", sizeof ("XXXX"));\r
+  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit4, ":", sizeof (":"));\r
+  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMajor, "yyy", sizeof ("yyy"));\r
+  CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMinor, "xxx", sizeof ("xxx"));\r
+  DHCPOpStart.End[0] = OP_END;\r
+};\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// DHCPDECLINE option structure\r
+//\r
+struct opdeclinestr {\r
+  UINT8                   DhcpCookie[4];\r
+  DHCPV4_OP_MESSAGE_TYPE  DhcpMessageType;\r
+  struct requestopendstr  OpDeclineEnd;\r
+};\r
+\r
+#define DHCPDECLINEoptions  (*(struct opdeclinestr *) DHCPV4_TRANSMIT_BUFFER.options)\r
+\r
+//\r
+// DHCPRELEASE option structure\r
+//\r
+struct opreleasestr {\r
+  UINT8                   DhcpCookie[4];\r
+  DHCPV4_OP_MESSAGE_TYPE  DhcpMessageType;\r
+  DHCPV4_OP_SERVER_IP     DhcServerIpPtr;\r
+  UINT8                   End[1];\r
+};\r
+\r
+#define DHCPRELEASEoptions  (*(struct opreleasestr *) DHCPV4_TRANSMIT_BUFFER.options)\r
+\r
+//\r
+// array of PXE vendor options in which we are interested\r
+// value 0 -> not of interest, else value is index into PXE OPTION array\r
+// option values from 1 to MAX_OUR_PXE_OPT\r
+//\r
+STATIC UINT8  ourPXEopts[MAX_OUR_PXE_OPT] = {\r
+  VEND_PXE_MTFTP_IP_IX,             // multicast IP address of bootfile for MTFTP listen\r
+  VEND_PXE_MTFTP_CPORT_IX,          // UDP Port to monitor for MTFTP responses - Intel order\r
+  VEND_PXE_MTFTP_SPORT_IX,          // Server UDP Port for MTFTP open - Intel order\r
+  VEND_PXE_MTFTP_TMOUT_IX,          // Listen timeout - secs\r
+  VEND_PXE_MTFTP_DELAY_IX,          // Transmission timeout - secs\r
+  VEND_PXE_DISCOVERY_CONTROL_IX,    // bit field\r
+  VEND_PXE_DISCOVERY_MCAST_ADDR_IX, // boot server discovery multicast address\r
+  VEND_PXE_BOOT_SERVERS_IX,         // list of boot servers of form tp(2) cnt(1) ips[cnt]\r
+  VEND_PXE_BOOT_MENU_IX,\r
+  VEND_PXE_BOOT_PROMPT_IX,\r
+  VEND_PXE_MCAST_ADDRS_ALLOC_IX,    // not used by client\r
+  VEND_PXE_CREDENTIAL_TYPES_IX,\r
+  VEND_13_IX,                       // not used by client\r
+  VEND_14_IX,                       // not used by client\r
+  VEND_15_IX,                       // not used by client\r
+  VEND_16_IX,                       // not used by client\r
+  VEND_17_IX,                       // not used by client\r
+  VEND_18_IX,                       // not used by client\r
+  VEND_19_IX,                       // not used by client\r
+  VEND_20_IX,                       // not used by client\r
+  VEND_21_IX,                       // not used by client\r
+  VEND_22_IX,                       // not used by client\r
+  VEND_23_IX,                       // not used by client\r
+  VEND_24_IX,                       // not used by client\r
+  VEND_25_IX,                       // not used by client\r
+  VEND_26_IX,                       // not used by client\r
+  VEND_27_IX,                       // not used by client\r
+  VEND_28_IX,                       // not used by client\r
+  VEND_29_IX,                       // not used by client\r
+  VEND_30_IX,                       // not used by client\r
+  VEND_31_IX,                       // not used by client\r
+  VEND_32_IX,                       // not used by client\r
+  VEND_33_IX,                       // not used by client\r
+  VEND_34_IX,                       // not used by client\r
+  VEND_35_IX,                       // not used by client\r
+  VEND_36_IX,                       // not used by client\r
+  VEND_37_IX,                       // not used by client\r
+  VEND_38_IX,                       // not used by client\r
+  VEND_39_IX,                       // not used by client\r
+  VEND_40_IX,                       // not used by client\r
+  VEND_41_IX,                       // not used by client\r
+  VEND_42_IX,                       // not used by client\r
+  VEND_43_IX,                       // not used by client\r
+  VEND_44_IX,                       // not used by client\r
+  VEND_45_IX,                       // not used by client\r
+  VEND_46_IX,                       // not used by client\r
+  VEND_47_IX,                       // not used by client\r
+  VEND_48_IX,                       // not used by client\r
+  VEND_49_IX,                       // not used by client\r
+  VEND_50_IX,                       // not used by client\r
+  VEND_51_IX,                       // not used by client\r
+  VEND_52_IX,                       // not used by client\r
+  VEND_53_IX,                       // not used by client\r
+  VEND_54_IX,                       // not used by client\r
+  VEND_55_IX,                       // not used by client\r
+  VEND_56_IX,                       // not used by client\r
+  VEND_57_IX,                       // not used by client\r
+  VEND_58_IX,                       // not used by client\r
+  VEND_59_IX,                       // not used by client\r
+  VEND_60_IX,                       // not used by client\r
+  VEND_61_IX,                       // not used by client\r
+  VEND_62_IX,                       // not used by client\r
+  VEND_63_IX,                       // not used by client\r
+  VEND_64_IX,                       // not used by client\r
+  VEND_65_IX,                       // not used by client\r
+  VEND_66_IX,                       // not used by client\r
+  VEND_67_IX,                       // not used by client\r
+  VEND_68_IX,                       // not used by client\r
+  VEND_69_IX,                       // not used by client\r
+  VEND_70_IX,                       // not used by client\r
+  VEND_PXE_BOOT_ITEM_IX\r
+};\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// array of options in which we are interested\r
+// value 0 -> not of interest, else value is index into OPTION array\r
+// option values from 1 to MAX_OUR_OPT\r
+//\r
+STATIC UINT8  OurDhcpOptions[MAX_OUR_OPT] = {\r
+  OP_SUBNET_MASK_IX,                      // OP_SUBNET_MASK   1   // data is the subnet mask\r
+  OP_TIME_OFFSET_IX,                      // OP_TIME_OFFSET   2   // data is the time offset of subnet to UTC in seconds\r
+  OP_ROUTER_LIST_IX,                      // OP_ROUTER_LIST   3   // list of routers on subnet\r
+  OP_TIME_SERVERS_IX,                     // OP_TIME_SERVERS  4   // list of time servers available\r
+  OP_NAME_SERVERS_IX,                     // OP_NAME_SERVERS  5   // list of name servers available\r
+  OP_DNS_SERVERS_IX,                      // OP_DNS_SERVERS   6   // list of DNS servers available\r
+  OP_LOG_SERVERS_IX,                      // OP_LOG_SERVERS   7\r
+  OP_COOKIE_SERVERS_IX,                   // OP_COOKIE_SERVERS    8\r
+  OP_LPR_SREVERS_IX,                      // OP_LPR_SREVERS   9\r
+  OP_IMPRESS_SERVERS_IX,                  // OP_IMPRESS_SERVERS   10\r
+  OP_RES_LOC_SERVERS_IX,                  // OP_RES_LOC_SERVERS   11\r
+  OP_HOST_NAME_IX,                        // OP_HOST_NAME 12  // client name\r
+  OP_BOOT_FILE_SZ_IX,                     // OP_BOOT_FILE_SZ  13  // number of 512 blocks of boot file\r
+  OP_DUMP_FILE_IX,                        // OP_DUMP_FILE 14  // path name of dump file if client crashes\r
+  OP_DOMAIN_NAME_IX,                      // OP_DOMAIN_NAME   15  // domain name to use\r
+  OP_SWAP_SERVER_IX,                      // OP_SWAP_SERVER   16\r
+  OP_ROOT_PATH_IX,                        // OP_ROOT_PATH 17  // path name containing root disk\r
+  OP_EXTENSION_PATH_IX,                   // OP_EXTENSION_PATH    18  // name of TFTP downloadable file of form of OP\r
+  OP_IP_FORWARDING_IX,                    // OP_IP_FORWARDING 19  // enable/disable IP packet forwarding\r
+  OP_NON_LOCAL_SRC_RTE_IX,                // OP_NON_LOCAL_SRC_RTE 20  // enable/disable non local source routing\r
+  OP_POLICY_FILTER_IX,                    // OP_POLICY_FILTER 21  // policy filters for non local source routing\r
+  OP_MAX_DATAGRAM_SZ_IX,                  // OP_MAX_DATAGRAM_SZ   22  // maximum datagram reassembly size\r
+  OP_DEFAULT_TTL_IX,                      // OP_DEFAULT_TTL   23  // default IP time to live\r
+  OP_MTU_AGING_TIMEOUT_IX,                // OP_MTU_AGING_TIMEOUT 24\r
+  OP_MTU_SIZES_IX,                        // OP_MTU_SIZES 25\r
+  OP_MTU_TO_USE_IX,                       // OP_MTU_TO_USE    26\r
+  OP_ALL_SUBNETS_LOCAL_IX,                // OP_ALL_SUBNETS_LOCAL 27\r
+  OP_BROADCAST_ADD_IX,                    // OP_BROADCAST_ADD 28  // broadcast address used on subnet\r
+  OP_PERFORM_MASK_DISCOVERY_IX,           // OP_PERFORM_MASK_DISCOVERY    29  // perform mask discovery using ICMP\r
+  OP_RESPOND_TO_MASK_REQ_IX,              // OP_RESPOND_TO_MASK_REQ   30  // respond to subnet mask requests using ICMP\r
+  OP_PERFORM_ROUTER_DISCOVERY_IX,         // OP_PERFORM_ROUTER_DISCOVERY  31\r
+  OP_ROUTER_SOLICIT_ADDRESS_IX,           // OP_ROUTER_SOLICIT_ADDRESS    32\r
+  OP_STATIC_ROUTER_LIST_IX,               // OP_STATIC_ROUTER_LIST    33  // list of dest/route pairs\r
+  OP_USE_ARP_TRAILERS_IX,                 // OP_USE_ARP_TRAILERS      34\r
+  OP_ARP_CACHE_TIMEOUT_IX,                // OP_ARP_CACHE_TIMEOUT 35\r
+  OP_ETHERNET_ENCAPSULATION_IX,           // OP_ETHERNET_ENCAPSULATION    36  // 0 -> RFC 894, 1 -> IEEE 802.3 (RFC 1042)\r
+  OP_TCP_DEFAULT_TTL_IX,                  // OP_TCP_DEFAULT_TTL   37  // default time to live when sending TCP segments\r
+  OP_TCP_KEEP_ALIVE_INT_IX,               // OP_TCP_KEEP_ALIVE_INT    38  // keep alive interval in seconds\r
+  OP_KEEP_ALIVE_GARBAGE_IX,               // OP_KEEP_ALIVE_GARBAGE    39\r
+  OP_NIS_DOMAIN_NAME_IX,                  // OP_NIS_DOMAIN_NAME   40\r
+  OP_NIS_SERVERS_IX,                      // OP_NIS_SERVERS   41\r
+  OP_NTP_SERVERS_IX,                      // OP_NTP_SERVERS   42\r
+  OP_VENDOR_SPECIFIC_IX,                  // OP_VENDOR_SPECIFIC   43\r
+  OP_NBNS_SERVERS_IX,                     // OP_NBNS_SERVERS  44\r
+  OP_NBDD_SERVERS_IX,                     // OP_NBDD_SERVERS  45\r
+  OP_NETBIOS_NODE_TYPE_IX,                // OP_NETBIOS_NODE_TYPE 46\r
+  OP_NETBIOS_SCOPE_IX,                    // OP_NETBIOS_SCOPE 47\r
+  OP_XWINDOW_SYSTEM_FONT_SERVERS_IX,      // OP_XWINDOW_SYSTEM_FONT_SERVERS   48\r
+  OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS_IX,  // OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS   49\r
+  OP_DHCP_REQ_IP_ADD_IX,                  // OP_DHCP_REQ_IP_ADD   50  // requested IP address - in DHCPDISCOVER\r
+  OP_DHCP_LEASE_TIME_IX,                  // OP_DHCP_LEASE_TIME   51  // lease time requested/granted\r
+  OP_DHCP_OPTION_OVERLOAD_IX,             // OP_DHCP_OPTION_OVERLOAD  52  // file/server name/both used to hold options\r
+  OP_DHCP_MESSAGE_TYPE_IX,                // OP_DHCP_MESSAGE_TYPE 53  // message type\r
+  OP_DHCP_SERVER_IP_IX,                   // OP_DHCP_SERVER_IP    54      // IP of server\r
+  OP_DHCP_PARM_REQ_LIST_IX,               // OP_DHCP_PARM_REQ_LIST    55  // list of requested parameters\r
+  OP_DHCP_ERROR_MESSAGE_IX,               // OP_DHCP_ERROR_MESSAGE    56  // in DHCPNAK or DECLINE messages\r
+  OP_DHCP_MAX_MESSAGE_SZ_IX,              // OP_DHCP_MAX_MESSAGE_SZ   57  // maximum DHCP message size client will accept\r
+  OP_DHCP_RENEWAL_TIME_IX,                // OP_DHCP_RENEWAL_TIME 58  // time in seconds before transitioning to RENEWING state\r
+  OP_DHCP_REBINDING_TIME_IX,              // OP_DHCP_REBINDING_TIME   59  // time in seconds before transitioning to REBINDING state\r
+  OP_DHCP_CLASS_IDENTIFIER_IX,            // OP_DHCP_CLASS_IDENTIFIER 60\r
+  OP_DHCP_CLIENT_IDENTIFIER_IX,           // OP_DHCP_CLIENT_IDENTIFIER    61\r
+  OP_RESERVED62_IX,                       // OP_RESERVED62\r
+  OP_RESERVED63_IX,                       // OP_RESERVED63\r
+  OP_NISPLUS_DOMAIN_NAME_IX,              // OP_NISPLUS_DOMAIN_NAME   64\r
+  OP_NISPLUS_SERVERS_IX,                  // OP_NISPLUS_SERVERS   65\r
+  OP_DHCP_TFTP_SERVER_NAME_IX,            // OP_DHCP_TFTP_SERVER_NAME 66\r
+  OP_DHCP_BOOTFILE_IX                     // OP_DHCP_BOOTFILE 67\r
+};\r
+\r
+#define RxBuf ((DHCP_RECEIVE_BUFFER *) (Private->ReceiveBuffers))\r
+\r
+#pragma pack()\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @param  Smbios              Pointer to SMBIOS structure\r
+  @param  StringNumber        String number to return. 0 is used to skip all\r
+                              strings and  point to the next SMBIOS structure.\r
+\r
+  @return Pointer to string, or pointer to next SMBIOS strcuture if StringNumber == 0\r
+\r
+**/\r
+CHAR8 *\r
+PxeBcLibGetSmbiosString (\r
+  IN  SMBIOS_STRUCTURE_POINTER  *Smbios,\r
+  IN  UINT16                    StringNumber\r
+  )\r
+{\r
+  UINT16  Index;\r
+  CHAR8   *String;\r
+\r
+  //\r
+  // Skip over formatted section\r
+  //\r
+  String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);\r
+\r
+  //\r
+  // Look through unformated section\r
+  //\r
+  for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {\r
+    if (StringNumber == Index) {\r
+      return String;\r
+    }\r
+    //\r
+    // Skip string\r
+    //\r
+    for (; *String != 0; String++)\r
+      ;\r
+    String++;\r
+\r
+    if (*String == 0) {\r
+      //\r
+      // If double NULL then we are done.\r
+      //  Return pointer to next structure in Smbios.\r
+      //  if you pass in a 0 you will always get here\r
+      //\r
+      Smbios->Raw = (UINT8 *)++String;\r
+      return NULL;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  This function gets system guid and serial number from the smbios table\r
+\r
+  @param  SystemGuid          The pointer of returned system guid\r
+  @param  SystemSerialNumber  The pointer of returned system serial number\r
+\r
+  @retval EFI_SUCCESS         Successfully get the system guid and system serial\r
+                              number\r
+  @retval EFI_NOT_FOUND       Not find the SMBIOS table\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcLibGetSmbiosSystemGuidAndSerialNumber (\r
+  IN  EFI_GUID  *SystemGuid,\r
+  OUT CHAR8     **SystemSerialNumber\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  SMBIOS_STRUCTURE_TABLE    *SmbiosTable;\r
+  SMBIOS_STRUCTURE_POINTER  Smbios;\r
+  SMBIOS_STRUCTURE_POINTER  SmbiosEnd;\r
+  UINT16                    Index;\r
+\r
+  Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Smbios.Hdr    = (SMBIOS_HEADER *) (UINTN) SmbiosTable->TableAddress;\r
+  SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);\r
+\r
+  for (Index = 0; Index < SmbiosTable->TableLength; Index++) {\r
+    if (Smbios.Hdr->Type == 1) {\r
+      if (Smbios.Hdr->Length < 0x19) {\r
+        //\r
+        // Older version did not support Guid and Serial number\r
+        //\r
+        continue;\r
+      }\r
+      //\r
+      // SMBIOS tables are byte packed so we need to do a byte copy to\r
+      // prevend alignment faults on Itanium-based platform.\r
+      //\r
+      CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));\r
+      *SystemSerialNumber = PxeBcLibGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+    //\r
+    // Make Smbios point to the next record\r
+    //\r
+    PxeBcLibGetSmbiosString (&Smbios, 0);\r
+\r
+    if (Smbios.Raw >= SmbiosEnd.Raw) {\r
+      //\r
+      // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.\r
+      // given this we must double check against the lenght of\r
+      // the structure.\r
+      //\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// add router list to list\r
+//\r
+STATIC\r
+VOID\r
+Ip4AddRouterList (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  DHCPV4_OP_IP_LIST   *IpListPtr\r
+  )\r
+{\r
+  EFI_IP_ADDRESS  TmpIp;\r
+  INTN            Index;\r
+  INTN            num;\r
+\r
+  if (IpListPtr == NULL) {\r
+    return ;\r
+  }\r
+\r
+  for (Index = 0, num = IpListPtr->Header.Length >> 2; Index < num; ++Index) {\r
+    CopyMem (&TmpIp, &IpListPtr->IpList[Index], 4);\r
+    Ip4AddRouter (Private, &TmpIp);\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// send ARP for our IP - fail if someone has it\r
+//\r
+STATIC\r
+BOOLEAN\r
+SetStationIP (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  EFI_MAC_ADDRESS DestMac;\r
+  EFI_STATUS      EfiStatus;\r
+\r
+  ZeroMem (&DestMac, sizeof DestMac);\r
+\r
+  if (GetHwAddr(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac)\r
+    || DoArp(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac) == EFI_SUCCESS) {\r
+    return FALSE;   // somebody else has this IP\r
+  }\r
+\r
+  CopyMem (\r
+    (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->StationIp,\r
+    &DHCP_REQ_OPTIONS.OpReqIP.Ip,\r
+    sizeof (EFI_IPv4_ADDRESS)\r
+    );\r
+\r
+  Private->GoodStationIp = TRUE;\r
+\r
+  if (!Private->UseIgmpv1Reporting) {\r
+    return TRUE;\r
+  }\r
+\r
+  if (Private->Igmpv1TimeoutEvent != NULL) {\r
+    return TRUE;\r
+  }\r
+\r
+  EfiStatus = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &Private->Igmpv1TimeoutEvent\r
+                    );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    Private->Igmpv1TimeoutEvent = NULL;\r
+    return TRUE;\r
+  }\r
+\r
+  EfiStatus = gBS->SetTimer (\r
+                    Private->Igmpv1TimeoutEvent,\r
+                    TimerRelative,\r
+                    (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000\r
+                    );  /* 400 seconds */\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    gBS->CloseEvent (Private->Igmpv1TimeoutEvent);\r
+    Private->Igmpv1TimeoutEvent = NULL;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+VOID\r
+AddRouters (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  DHCP_RECEIVE_BUFFER *RxBufPtr\r
+  )\r
+{\r
+  Ip4AddRouterList (\r
+    Private,\r
+    (DHCPV4_OP_IP_LIST *) RxBufPtr->OpAdds.PktOptAdds[OP_ROUTER_LIST_IX - 1]\r
+    );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+EFI_STATUS\r
+DoUdpWrite (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
+  EFI_IP_ADDRESS              *ClientIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ClientPortPtr\r
+  )\r
+{\r
+  UINTN Len;\r
+\r
+  Len = sizeof DHCPV4_TRANSMIT_BUFFER;\r
+\r
+  return UdpWrite (\r
+          Private,\r
+          EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
+          ServerIpPtr,\r
+          ServerPortPtr,\r
+          0,\r
+          ClientIpPtr,\r
+          ClientPortPtr,\r
+          0,\r
+          0,\r
+          &Len,\r
+          Private->TransmitBuffer\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// initialize the DHCP structure\r
+//\r
+typedef struct {\r
+  UINT8 x[4];\r
+} C4Str;\r
+\r
+STATIC\r
+VOID\r
+InitDhcpv4TxBuf (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  UINTN                   HwAddrLen;\r
+  UINT8                   *String;\r
+  CHAR8                   *SystemSerialNumber;\r
+  EFI_PXE_BASE_CODE_MODE  *PxebcMode;\r
+\r
+  PxebcMode = Private->EfiBc.Mode;\r
+\r
+  ZeroMem (&DHCPV4_TRANSMIT_BUFFER, sizeof (DHCPV4_STRUCT));\r
+  DHCPV4_TRANSMIT_BUFFER.op     = BOOTP_REQUEST;\r
+  DHCPV4_TRANSMIT_BUFFER.htype  = Private->SimpleNetwork->Mode->IfType;\r
+  DHCPV4_TRANSMIT_BUFFER.flags  = HTONS (DHCP_BROADCAST_FLAG);\r
+  CopyMem (&DHCPV4_OPTIONS_BUFFER, (VOID *) &DHCPOpStart, sizeof (DHCPOpStart));\r
+\r
+  //\r
+  // default to hardware address\r
+  //\r
+  HwAddrLen = Private->SimpleNetwork->Mode->HwAddressSize;\r
+\r
+  if (HwAddrLen > sizeof DHCPV4_TRANSMIT_BUFFER.chaddr) {\r
+    HwAddrLen = sizeof DHCPV4_TRANSMIT_BUFFER.chaddr;\r
+  }\r
+\r
+  String = (UINT8 *) &Private->SimpleNetwork->Mode->CurrentAddress;\r
+\r
+  if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber (\r
+        (EFI_GUID *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid,\r
+        &SystemSerialNumber\r
+        ) == EFI_SUCCESS) {\r
+    if (PxebcMode->SendGUID) {\r
+      HwAddrLen = sizeof (EFI_GUID);\r
+      String    = (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid;\r
+    }\r
+  } else {\r
+    //\r
+    // GUID not yet set - send all 0xff's to show programable (via SetVariable)\r
+    // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);\r
+    // GUID not yet set - send all 0's to show not programable\r
+    //\r
+    ZeroMem (DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof (EFI_GUID));\r
+  }\r
+\r
+  DHCPV4_TRANSMIT_BUFFER.hlen = (UINT8) HwAddrLen;\r
+  CopyMem (DHCPV4_TRANSMIT_BUFFER.chaddr, String, HwAddrLen);\r
+\r
+  CvtNum (\r
+    SYS_ARCH,\r
+    (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType,\r
+    sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType\r
+    );\r
+\r
+  DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.Type                         = Private->NiiPtr->Type;\r
+  DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion                 = Private->NiiPtr->MajorVer;\r
+  DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion                 = Private->NiiPtr->MinorVer;\r
+\r
+  *(C4Str *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.InterfaceName = *(C4Str *) Private->NiiPtr->StringId;\r
+\r
+  CvtNum (\r
+    DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion,\r
+    (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor,\r
+    sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor\r
+    );\r
+\r
+  CvtNum (\r
+    DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion,\r
+    (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor,\r
+    sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor\r
+    );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+UINT32\r
+DecodePxeOptions (\r
+  DHCP_RECEIVE_BUFFER *RxBufPtr,\r
+  UINT8               *ptr,\r
+  INTN                Len\r
+  )\r
+{\r
+  UINT8     Op;\r
+  UINT8     *EndPtr;\r
+  INTN      Index;\r
+  UNION_PTR LocalPtr;\r
+  UINT32    status;\r
+\r
+  status = 0;\r
+\r
+  for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) {\r
+    Op  = ptr[0];\r
+    Len = ptr[1];\r
+\r
+    switch (Op) {\r
+    case OP_PAD:\r
+      Len = -1;\r
+      break;\r
+\r
+    case OP_END:\r
+      return status;\r
+\r
+    default:\r
+      LocalPtr.BytePtr = ptr;\r
+      if (Op <= MAX_OUR_PXE_OPT) {\r
+        Index = ourPXEopts[Op - 1];\r
+        if (Index) {\r
+          RxBufPtr->OpAdds.PxeOptAdds[Index - 1] = LocalPtr.OpPtr;\r
+          status |= 1 << Index;\r
+          if (Index == VEND_PXE_BOOT_ITEM && LocalPtr.BootItem->Header.Length == 3) {\r
+            RxBufPtr->OpAdds.Status |= USE_THREE_BYTE;\r
+          }\r
+        }\r
+      }\r
+      break;\r
+    }\r
+  }\r
+\r
+  return status;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+VOID\r
+DecodeOptions (\r
+  DHCP_RECEIVE_BUFFER *RxBufPtr,\r
+  UINT8               *ptr,\r
+  INTN                Len\r
+  )\r
+{\r
+  UINT8     Op;\r
+  UINT8     *EndPtr;\r
+  INTN      Index;\r
+  UNION_PTR LocalPtr;\r
+\r
+  for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) {\r
+    Op  = ptr[0];\r
+    Len = ptr[1];\r
+\r
+    switch (Op) {\r
+    case OP_PAD:\r
+      Len = -1;\r
+      break;\r
+\r
+    case OP_END:\r
+      return ;\r
+\r
+    default:\r
+      LocalPtr.BytePtr = ptr;\r
+      if (Op <= MAX_OUR_OPT) {\r
+        Index = OurDhcpOptions[Op - 1];\r
+        if (Index) {\r
+          RxBufPtr->OpAdds.PktOptAdds[Index - 1] = LocalPtr.OpPtr;\r
+          if (Index == OP_VENDOR_SPECIFIC_IX) {\r
+            UINT32  status;\r
+            status = DecodePxeOptions (\r
+                      RxBufPtr,\r
+                      (UINT8 *) LocalPtr.VendorOptions->VendorOptions,\r
+                      LocalPtr.VendorOptions->Header.Length\r
+                      );\r
+            if (status) {\r
+              RxBufPtr->OpAdds.Status |= PXE_TYPE;\r
+              //\r
+              // check for all the MTFTP info options present - any missing is a nogo\r
+              //\r
+              if ((status & WfM11a_OPTS) == WfM11a_OPTS) {\r
+                RxBufPtr->OpAdds.Status |= WfM11a_TYPE;\r
+              }\r
+\r
+              if (status & DISCOVER_OPTS) {\r
+                RxBufPtr->OpAdds.Status |= DISCOVER_TYPE;\r
+              }\r
+\r
+              if (status & CREDENTIALS_OPT) {\r
+                RxBufPtr->OpAdds.Status |= CREDENTIALS_TYPE;\r
+              }\r
+            }\r
+          }\r
+        }\r
+      }\r
+      break;\r
+    }\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+Parse (\r
+  DHCP_RECEIVE_BUFFER *RxBufPtr,\r
+  INTN                Len\r
+  )\r
+{\r
+  UNION_PTR LocalPtr;\r
+\r
+  //\r
+  // initialize\r
+  //\r
+  SetMem (&RxBufPtr->OpAdds, sizeof RxBufPtr->OpAdds, 0);\r
+\r
+  DecodeOptions (\r
+    RxBufPtr,\r
+    RxBufPtr->u.Dhcpv4.options + 4,\r
+    Len - (sizeof RxBufPtr->u.Dhcpv4 - sizeof RxBufPtr->u.Dhcpv4.options + 4)\r
+    );\r
+\r
+  LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_OPTION_OVERLOAD_IX - 1];\r
+\r
+  if ((LocalPtr.OpPtr) && (LocalPtr.Overload->Overload & OVLD_SRVR_NAME)) {\r
+    DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.sname, sizeof RxBufPtr->u.Dhcpv4.sname);\r
+  }\r
+\r
+  if (LocalPtr.OpPtr && (LocalPtr.Overload->Overload & OVLD_FILE)) {\r
+    DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.file, sizeof RxBufPtr->u.Dhcpv4.file);\r
+  } else if (!RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && RxBufPtr->u.Dhcpv4.file[0]) {\r
+    RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] = (DHCPV4_OP_STRUCT *) (RxBufPtr->u.Dhcpv4.file - sizeof (DHCPV4_OP_HEADER));\r
+\r
+    RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length = (UINT8) AsciiStrLen (RxBufPtr->u.Dhcpv4.file);\r
+  }\r
+\r
+  LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_CLASS_IDENTIFIER_IX - 1];\r
+\r
+  if ((LocalPtr.OpPtr) &&\r
+      LocalPtr.PxeClassStr->Header.Length >= 9 &&\r
+      !CompareMem (LocalPtr.PxeClassStr->Class, "PXEClient", 9)\r
+        ) {\r
+    RxBufPtr->OpAdds.Status |= PXE_TYPE;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+VOID\r
+CopyParseRxBuf (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  INTN                RxBufIndex,\r
+  INTN                PacketIndex\r
+  )\r
+{\r
+  DHCP_RECEIVE_BUFFER *RxBufPtr;\r
+\r
+  RxBufPtr = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[PacketIndex];\r
+\r
+  CopyMem (\r
+    &RxBufPtr->u.Dhcpv4,\r
+    &RxBuf[RxBufIndex].u.Dhcpv4,\r
+    sizeof (RxBuf[RxBufIndex].u.Dhcpv4)\r
+    );\r
+\r
+  Parse (RxBufPtr, sizeof RxBufPtr->u.ReceiveBuffer);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+VOID\r
+CopyProxyRxBuf (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  INTN                RxBufIndex\r
+  )\r
+{\r
+  Private->EfiBc.Mode->ProxyOfferReceived = TRUE;\r
+  CopyParseRxBuf (Private, RxBufIndex, PXE_OFFER_INDEX);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+VOID\r
+CopyParse (\r
+  PXE_BASECODE_DEVICE       *Private,\r
+  EFI_PXE_BASE_CODE_PACKET  *PacketPtr,\r
+  EFI_PXE_BASE_CODE_PACKET  *NewPacketPtr,\r
+  INTN                      Index\r
+  )\r
+{\r
+  DHCP_RECEIVE_BUFFER *DhcpRxBuf;\r
+\r
+  DhcpRxBuf = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[Index];\r
+\r
+  CopyMem (\r
+    (EFI_PXE_BASE_CODE_PACKET *) &DhcpRxBuf->u.Dhcpv4,\r
+    NewPacketPtr,\r
+    sizeof (*NewPacketPtr)\r
+    );\r
+\r
+  CopyMem (&*PacketPtr, &*NewPacketPtr, sizeof (*NewPacketPtr));\r
+\r
+  Parse (DhcpRxBuf, sizeof DhcpRxBuf->u.ReceiveBuffer);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+BOOLEAN\r
+AckEdit (\r
+  DHCP_RECEIVE_BUFFER *DhcpRxBuf\r
+  )\r
+{\r
+  UNION_PTR LocalPtr;\r
+\r
+  LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1];\r
+\r
+  //\r
+  // check that an ACK\r
+  // if a DHCP type, must be DHCPOFFER and must have server id\r
+  //\r
+  return (BOOLEAN)\r
+    (\r
+      (LocalPtr.OpPtr) &&\r
+      (LocalPtr.MessageType->Type == DHCPACK) &&\r
+      DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]\r
+    );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// if a discover type packet, make sure all required fields are present\r
+//\r
+BOOLEAN\r
+DHCPOfferAckEdit (\r
+  DHCP_RECEIVE_BUFFER *DhcpRxBuf\r
+  )\r
+{\r
+  PXE_OP_SERVER_LIST  *BootServerOpPtr;\r
+  UNION_PTR           LocalPtr;\r
+\r
+  if ((DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) == 0) {\r
+    return TRUE;\r
+  }\r
+\r
+  LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];\r
+\r
+  if (LocalPtr.OpPtr == NULL) {\r
+    LocalPtr.OpPtr  = (DHCPV4_OP_STRUCT *) &DefaultDisCtl;\r
+    DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultDisCtl;\r
+  }\r
+  //\r
+  // make sure all required fields are here\r
+  // if mucticast enabled, need multicast address\r
+  //\r
+  if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST) &&\r
+      (!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))\r
+      ) {\r
+    return FALSE;\r
+    //\r
+    // missing required field\r
+    //\r
+  }\r
+  //\r
+  // if a list, it better be good\r
+  //\r
+  BootServerOpPtr = (PXE_OP_SERVER_LIST *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1];\r
+\r
+  if (BootServerOpPtr != NULL) {\r
+    PXE_SERVER_LIST *BootServerListPtr;\r
+    INTN            ServerListLen;\r
+    INTN            ServerEntryLen;\r
+\r
+    BootServerListPtr = BootServerOpPtr->ServerList;\r
+    ServerListLen     = BootServerOpPtr->Header.Length;\r
+\r
+    do {\r
+      EFI_IPv4_ADDRESS  *IpListPtr;\r
+      INTN              IpCnt;\r
+\r
+      IpCnt           = BootServerListPtr->u.Ipv4List.IpCount;\r
+\r
+      ServerEntryLen  = sizeof (PXEV4_SERVER_LIST) + 2 + (IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS);\r
+\r
+      if (ServerListLen < ServerEntryLen) {\r
+        //\r
+        // missing required field\r
+        //\r
+        return FALSE;\r
+      }\r
+\r
+      IpListPtr = BootServerListPtr->u.Ipv4List.IpList;\r
+\r
+      while (IpCnt--) {\r
+        if (IS_MULTICAST (IpListPtr)) {\r
+          //\r
+          // missing required field\r
+          //\r
+          return FALSE;\r
+        } else {\r
+          ++IpListPtr;\r
+        }\r
+      }\r
+\r
+      BootServerListPtr = (PXE_SERVER_LIST *) IpListPtr;\r
+    } while (ServerListLen -= ServerEntryLen);\r
+  }\r
+  //\r
+  // else there must be a list if use list enabled or multicast and\r
+  // broadcast disabled\r
+  //\r
+  else if ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) ||\r
+           ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST))\r
+          ) {\r
+    //\r
+    // missing required field\r
+    //\r
+    return FALSE;\r
+  }\r
+  //\r
+  // if not USE_BOOTFILE or no bootfile given, must have menu stuff\r
+  //\r
+  if (!(LocalPtr.DiscoveryControl->ControlBits & USE_BOOTFILE) ||\r
+      !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]\r
+      ) {\r
+    INTN  MenuLth;\r
+\r
+    LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];\r
+\r
+    if (LocalPtr.OpPtr == NULL || !DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]) {\r
+      //\r
+      // missing required field\r
+      //\r
+      return FALSE;\r
+    }\r
+    //\r
+    // make sure menu valid\r
+    //\r
+    MenuLth               = LocalPtr.BootMenu->Header.Length;\r
+    LocalPtr.BootMenuItem = LocalPtr.BootMenu->MenuItem;\r
+\r
+    do {\r
+      INTN  MenuItemLen;\r
+\r
+      MenuItemLen = LocalPtr.BootMenuItem->DataLen;\r
+\r
+      if (MenuItemLen == 0) {\r
+        //\r
+        // missing required field\r
+        //\r
+        return FALSE;\r
+      }\r
+\r
+      MenuItemLen += sizeof (*LocalPtr.BootMenuItem) - sizeof (LocalPtr.BootMenuItem->Data);\r
+\r
+      MenuLth -= MenuItemLen;\r
+      LocalPtr.BytePtr += MenuItemLen;\r
+    } while (MenuLth > 0);\r
+\r
+    if (MenuLth != 0) {\r
+      //\r
+      // missing required field\r
+      //\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  if (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) {\r
+    DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultBootItem;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+BOOLEAN\r
+DHCPAckEdit (\r
+  DHCP_RECEIVE_BUFFER *RxBufPtr\r
+  )\r
+{\r
+  return (BOOLEAN) (DHCPOfferAckEdit (RxBufPtr) ? AckEdit (RxBufPtr) : FALSE);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// get an offer/ack\r
+//\r
+EFI_STATUS\r
+GetOfferAck (\r
+  PXE_BASECODE_DEVICE          *Private,\r
+  BOOLEAN                     (*ExtraEdit)(DHCP_RECEIVE_BUFFER *DhcpRxBuf),\r
+  UINT16 OpFlags, // for Udp read\r
+  EFI_IP_ADDRESS *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,\r
+  EFI_IP_ADDRESS *ClientIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT *ClientPortPtr,\r
+  DHCP_RECEIVE_BUFFER *DhcpRxBuf,\r
+  EFI_EVENT TimeoutEvent\r
+  )\r
+/*++\r
+Routine description:\r
+  Wait for an OFFER/ACK packet.\r
+\r
+Parameters:\r
+  Private := Pointer to PxeBc interface\r
+  ExtraEdit := Pointer to extra option checking function\r
+  OpFlags := UdpRead() option flags\r
+  ServerIpPtr :=\r
+  ServerPortPtr :=\r
+  ClientIpPtr :=\r
+  ClientPortPtr :=\r
+  DhcpRxBuf :=\r
+  TimeoutEvent :=\r
+\r
+Returns:\r
+--*/\r
+{\r
+  EFI_IP_ADDRESS  ServerIp;\r
+  EFI_STATUS      StatCode;\r
+  INTN            RxBufLen;\r
+\r
+  for (;;) {\r
+    //\r
+    // Wait until we get a UDP packet.\r
+    //\r
+    ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS));\r
+    RxBufLen = sizeof RxBuf[0].u.ReceiveBuffer;\r
+\r
+    if ((StatCode = UdpRead (\r
+                      Private,\r
+                      OpFlags,\r
+                      ClientIpPtr,\r
+                      ClientPortPtr,\r
+                      ServerIpPtr,\r
+                      ServerPortPtr,\r
+                      0,\r
+                      0,\r
+                      (UINTN *) &RxBufLen,\r
+                      &DhcpRxBuf->u.Dhcpv4,\r
+                      TimeoutEvent\r
+                      )) != EFI_SUCCESS) {\r
+      if (StatCode == EFI_TIMEOUT) {\r
+        StatCode = EFI_NO_RESPONSE;\r
+      }\r
+\r
+      break;\r
+    }\r
+    //\r
+    // got a packet - see if a good offer\r
+    //\r
+    if (DhcpRxBuf->u.Dhcpv4.op != BOOTP_REPLY) {\r
+      continue;\r
+    }\r
+\r
+    if (DhcpRxBuf->u.Dhcpv4.xid != DHCPV4_TRANSMIT_BUFFER.xid) {\r
+      continue;\r
+    }\r
+\r
+    if (*(UINT32 *) DHCPV4_TRANSMIT_BUFFER.options != * (UINT32 *) DhcpRxBuf->u.Dhcpv4.options) {\r
+      continue;\r
+    }\r
+\r
+    if (*(UINT8 *) &DhcpRxBuf->u.Dhcpv4.yiaddr > 223) {\r
+      continue;\r
+    }\r
+\r
+    if (CompareMem (\r
+          DhcpRxBuf->u.Dhcpv4.chaddr,\r
+          DHCPV4_TRANSMIT_BUFFER.chaddr,\r
+          sizeof DhcpRxBuf->u.Dhcpv4.chaddr\r
+          )) {\r
+      //\r
+      // no good\r
+      //\r
+      continue;\r
+    }\r
+\r
+    Parse (DhcpRxBuf, RxBufLen);\r
+\r
+    if (!(*ExtraEdit) (DhcpRxBuf)) {\r
+      continue;\r
+    }\r
+    //\r
+    // Good DHCP packet.\r
+    //\r
+    StatCode = EFI_SUCCESS;\r
+    break;\r
+  }\r
+\r
+  return StatCode;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// get DHCPOFFER's\r
+//\r
+EFI_STATUS\r
+GetOffers (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  EFI_IP_ADDRESS  ClientIp;\r
+  EFI_IP_ADDRESS  ServerIp;\r
+  EFI_STATUS      StatCode;\r
+  EFI_EVENT       TimeoutEvent;\r
+  INTN            NumOffers;\r
+  INTN            Index;\r
+\r
+  //\r
+  //\r
+  //\r
+  ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS));\r
+  NumOffers = 0;\r
+\r
+  for (Index = 0; Index < (sizeof Private->ServerCount) / sizeof Private->ServerCount[0]; ++Index) {\r
+    Private->ServerCount[Index] = 0;\r
+    Private->GotProxy[Index]    = 0;\r
+  }\r
+\r
+  Private->GotBootp               = 0;\r
+  //\r
+  // these we throw away\r
+  //\r
+  Private->GotProxy[DHCP_ONLY_IX] = 1;\r
+  StatCode = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &TimeoutEvent\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    return StatCode;\r
+  }\r
+\r
+  StatCode = gBS->SetTimer (\r
+                    TimeoutEvent,\r
+                    TimerRelative,\r
+                    Private->Timeout * 10000000 + 1000000\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return StatCode;\r
+  }\r
+  //\r
+  // get offers\r
+  //\r
+  for (;;) {\r
+    DHCP_RECEIVE_BUFFER *DhcpRxBuf;\r
+    UNION_PTR           LocalPtr;\r
+\r
+    DhcpRxBuf = &RxBuf[NumOffers];\r
+\r
+    if ((\r
+          StatCode = GetOfferAck (\r
+                  Private,\r
+        DHCPOfferAckEdit,\r
+        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |\r
+        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP |\r
+        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,\r
+        &ServerIp,\r
+        &DhcpServerPort,\r
+        &ClientIp,\r
+        &DHCPClientPort,\r
+        DhcpRxBuf,\r
+        TimeoutEvent\r
+        )\r
+) != EFI_SUCCESS\r
+        ) {\r
+      break;\r
+    }\r
+\r
+    LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1];\r
+\r
+    //\r
+    // check type of offer\r
+    //\r
+    if (LocalPtr.OpPtr == NULL) {\r
+      //\r
+      // bootp - we only need one and make sure has bootfile\r
+      //\r
+      if (Private->GotBootp || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {\r
+        continue;\r
+      }\r
+\r
+      Private->GotBootp = (UINT8) (NumOffers + 1);\r
+    }\r
+    //\r
+    // if a DHCP type, must be DHCPOFFER and must have server id\r
+    //\r
+    else if (LocalPtr.MessageType->Type != DHCPOFFER || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]) {\r
+      continue;\r
+    } else {\r
+      INTN  TypeIx;\r
+\r
+      //\r
+      // get type - PXE10, WfM11a, or BINL\r
+      //\r
+      if (DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) {\r
+        TypeIx = PXE10_IX;\r
+      } else if (DhcpRxBuf->OpAdds.Status & WfM11a_TYPE) {\r
+        //\r
+        // WfM - make sure it has a bootfile\r
+        //\r
+        if (!DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {\r
+          continue;\r
+        }\r
+\r
+        TypeIx = WfM11a_IX;\r
+      } else {\r
+        TypeIx = (DhcpRxBuf->OpAdds.Status & PXE_TYPE) ? BINL_IX : DHCP_ONLY_IX;\r
+      }\r
+      //\r
+      // check DHCP or proxy\r
+      //\r
+      if (DhcpRxBuf->u.Dhcpv4.yiaddr == 0) {\r
+        //\r
+        // proxy - only need one of each type if not BINL\r
+        // and must have at least PXE_TYPE\r
+        //\r
+        if (TypeIx == BINL_IX) {\r
+          Private->BinlProxies[Private->GotProxy[BINL_IX]++] = (UINT8) NumOffers;\r
+        } else if (Private->GotProxy[TypeIx]) {\r
+          continue;\r
+        } else {\r
+          Private->GotProxy[TypeIx] = (UINT8) (NumOffers + 1);\r
+        }\r
+      } else {\r
+        Private->OfferCount[TypeIx][Private->ServerCount[TypeIx]++] = (UINT8) NumOffers;\r
+      }\r
+    }\r
+\r
+    if (++NumOffers == MAX_OFFERS) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  gBS->CloseEvent (TimeoutEvent);\r
+  Private->NumOffersReceived = NumOffers;\r
+\r
+  return (Private->NumOffersReceived) ? EFI_SUCCESS : EFI_NO_RESPONSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// send DHCPDECLINE\r
+//\r
+STATIC\r
+VOID\r
+DeclineOffer (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxebcMode;\r
+  UINT16                  SaveSecs;\r
+\r
+  PxebcMode                     = Private->EfiBc.Mode;\r
+  SaveSecs                      = DHCPV4_TRANSMIT_BUFFER.secs;\r
+\r
+  DHCPV4_TRANSMIT_BUFFER.secs   = 0;\r
+  DHCPV4_TRANSMIT_BUFFER.flags  = 0;\r
+  SetMem (\r
+    DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opdeclinestr),\r
+    sizeof (DHCPOpStart) - sizeof (struct opdeclinestr),\r
+    OP_PAD\r
+    );\r
+  DHCPDECLINEoptions.DhcpMessageType.Type = DHCPDECLINE;\r
+  CopyMem (&DHCPDECLINEoptions.OpDeclineEnd, &DHCP_REQ_OPTIONS, sizeof (struct requestopendstr));\r
+\r
+  {\r
+    EFI_IP_ADDRESS  TmpIp;\r
+\r
+    CopyMem (&TmpIp, &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, sizeof TmpIp);\r
+\r
+    DoUdpWrite (\r
+      Private,\r
+      &TmpIp,\r
+      &DhcpServerPort,\r
+      &PxebcMode->StationIp,\r
+      &DHCPClientPort\r
+      );\r
+  }\r
+\r
+  InitDhcpv4TxBuf (Private);\r
+  DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;\r
+  Private->GoodStationIp      = FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// send DHCPRELEASE\r
+//\r
+STATIC\r
+BOOLEAN\r
+Release (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxebcMode;\r
+  UINT16                  SaveSecs;\r
+\r
+  PxebcMode                   = Private->EfiBc.Mode;\r
+  SaveSecs                    = DHCPV4_TRANSMIT_BUFFER.secs;\r
+  DHCPV4_TRANSMIT_BUFFER.secs = 0;\r
+\r
+  SetMem (\r
+    DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opreleasestr),\r
+    sizeof (DHCPOpStart) - sizeof (struct opreleasestr),\r
+    OP_PAD\r
+    );\r
+\r
+  DHCPRELEASEoptions.DhcpMessageType.Type = DHCPRELEASE;\r
+\r
+  CopyMem (\r
+    &DHCPRELEASEoptions.DhcServerIpPtr,\r
+    &(DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1],\r
+    sizeof DHCPRELEASEoptions.DhcServerIpPtr\r
+    );\r
+\r
+  DHCPRELEASEoptions.End[0] = OP_END;\r
+\r
+  {\r
+    EFI_IP_ADDRESS  TmpIp;\r
+\r
+    CopyMem (&TmpIp, &DHCPRELEASEoptions.DhcServerIpPtr.Ip, sizeof TmpIp);\r
+\r
+    DoUdpWrite (\r
+      Private,\r
+      &TmpIp,\r
+      &DhcpServerPort,\r
+      &PxebcMode->StationIp,\r
+      &DHCPClientPort\r
+      );\r
+  }\r
+\r
+  InitDhcpv4TxBuf (Private);\r
+\r
+  DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;\r
+  Private->GoodStationIp      = FALSE;\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+BOOLEAN\r
+GetBINLAck (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  EFI_IP_ADDRESS      *ServerIpPtr\r
+  )\r
+{\r
+  DHCP_RECEIVE_BUFFER *DhcpRxBuf;\r
+  EFI_STATUS          StatCode;\r
+  EFI_EVENT           TimeoutEvent;\r
+\r
+  //\r
+  //\r
+  //\r
+  StatCode = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &TimeoutEvent\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    return FALSE;\r
+  }\r
+\r
+  StatCode = gBS->SetTimer (\r
+                    TimeoutEvent,\r
+                    TimerRelative,\r
+                    Private->Timeout * 10000000 + 1000000\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return FALSE;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  DhcpRxBuf = &PXE_BINL_BUFFER;\r
+\r
+  for (;;) {\r
+    EFI_PXE_BASE_CODE_UDP_PORT  BINLSrvPort;\r
+\r
+    BINLSrvPort = 0;\r
+\r
+    if (GetOfferAck (\r
+          Private,\r
+          AckEdit,\r
+          EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,\r
+          ServerIpPtr,\r
+          &BINLSrvPort,\r
+          &Private->EfiBc.Mode->StationIp,\r
+          &PSEUDO_DHCP_CLIENT_PORT,\r
+          DhcpRxBuf,\r
+          TimeoutEvent\r
+          ) != EFI_SUCCESS) {\r
+      break;\r
+    }\r
+    //\r
+    // make sure from whom we wanted\r
+    //\r
+    if (!DhcpRxBuf->u.Dhcpv4.yiaddr && !CompareMem (\r
+                                          &ServerIpPtr->v4,\r
+                                          &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,\r
+                                          sizeof (ServerIpPtr->v4)\r
+                                          )) {\r
+      gBS->CloseEvent (TimeoutEvent);\r
+      //\r
+      // got an ACK from server\r
+      //\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  gBS->CloseEvent (TimeoutEvent);\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// make sure we can get BINL\r
+// send DHCPREQUEST to PXE server\r
+//\r
+STATIC\r
+BOOLEAN\r
+TryBINL (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  INTN                OfferIx\r
+  )\r
+{\r
+  DHCP_RECEIVE_BUFFER *DhcpRxBuf;\r
+  EFI_IP_ADDRESS      ServerIp;\r
+  UINT16              SaveSecs;\r
+  INTN                Index;\r
+\r
+  DhcpRxBuf = &RxBuf[OfferIx];\r
+\r
+  //\r
+  // use next server address first.\r
+  //\r
+  ServerIp.Addr[0] = DhcpRxBuf->u.Dhcpv4.siaddr;\r
+  if (ServerIp.Addr[0] == 0) {\r
+    //\r
+    // next server address is NULL, use option 54.\r
+    //\r
+    CopyMem (\r
+      ((EFI_IPv4_ADDRESS *) &ServerIp),\r
+      &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+  }\r
+\r
+  //\r
+  // client IP address - filled in by client if it knows it\r
+  //\r
+  CopyMem (\r
+    ((EFI_IPv4_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr),\r
+    &DHCP_REQ_OPTIONS.OpReqIP.Ip,\r
+    sizeof (EFI_IPv4_ADDRESS)\r
+    );\r
+\r
+  SetMem (&DHCP_REQ_OPTIONS, sizeof DHCP_REQ_OPTIONS, OP_PAD);\r
+  DHCPV4_TRANSMIT_BUFFER.flags  = 0;\r
+  DHCPV4_OPTIONS_BUFFER.End[0]  = OP_END;\r
+  AddRouters (Private, DhcpRxBuf);\r
+  SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs;\r
+\r
+  for (Index = 0; Index < 3; Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Index) {\r
+    DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);\r
+\r
+    //\r
+    // unicast DHCPREQUEST to PXE server\r
+    //\r
+    if (DoUdpWrite (\r
+          Private,\r
+          &ServerIp,\r
+          &PseudoDhcpServerPort,\r
+          (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,\r
+          &PSEUDO_DHCP_CLIENT_PORT\r
+          ) != EFI_SUCCESS) {\r
+      break;\r
+    }\r
+\r
+    if (!GetBINLAck (Private, &ServerIp)) {\r
+      continue;\r
+    }\r
+    //\r
+    // early exit failures\r
+    // make sure a good ACK\r
+    //\r
+    if (!DHCPOfferAckEdit (&PXE_BINL_BUFFER) || (\r
+          !(PXE_BINL_BUFFER.OpAdds.Status & DISCOVER_TYPE) && !PXE_BINL_BUFFER.OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]\r
+      )\r
+        ) {\r
+      break;\r
+    }\r
+\r
+    Private->EfiBc.Mode->ProxyOfferReceived = TRUE;\r
+    return TRUE;\r
+  }\r
+  //\r
+  // failed - reset seconds field, etc.\r
+  //\r
+  Private->EfiBc.Mode->RouteTableEntries = 0;\r
+  //\r
+  // reset\r
+  //\r
+  DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs;\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+BOOLEAN\r
+TryFinishBINL (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  INTN                OfferIx\r
+  )\r
+{\r
+  if (TryBINL (Private, OfferIx)) {\r
+    return TRUE;\r
+  }\r
+\r
+  return Release (Private);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+BOOLEAN\r
+TryFinishProxyBINL (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  INTN  Index;\r
+\r
+  for (Index = 0; Index < Private->GotProxy[BINL_IX]; ++Index) {\r
+    if (TryBINL (Private, Private->BinlProxies[Index])) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return Release (Private);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// try to finish DORA - send DHCP request, wait for ACK, check with ARP\r
+//\r
+STATIC\r
+BOOLEAN\r
+TryFinishDORA (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  INTN                OfferIx\r
+  )\r
+{\r
+  DHCP_RECEIVE_BUFFER *DhcpRxBuf;\r
+  EFI_IP_ADDRESS      ClientIp;\r
+  EFI_IP_ADDRESS      ServerIp;\r
+  EFI_STATUS          StatCode;\r
+  UNION_PTR           LocalPtr;\r
+  EFI_EVENT           TimeoutEvent;\r
+\r
+  //\r
+  // send DHCP request\r
+  // if fail return false\r
+  //\r
+  DhcpRxBuf = &DHCPV4_ACK_BUFFER;\r
+  DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type  = DHCPREQUEST;\r
+  CopyMem (&DHCP_REQ_OPTIONS, &RequestOpEndStr, sizeof (RequestOpEndStr));\r
+//  DHCP_REQ_OPTIONS = RequestOpEndStr;\r
+  DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[OfferIx].u.Dhcpv4.yiaddr;\r
+\r
+  CopyMem (\r
+    &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip,\r
+    &((DHCPV4_OP_SERVER_IP *) RxBuf[OfferIx].OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,\r
+    sizeof DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip\r
+    );\r
+\r
+  CopyMem (\r
+    Private->EfiBc.Mode->SubnetMask.Addr,\r
+    &DefaultSubnetMask,\r
+    4\r
+    );\r
+\r
+  //\r
+  // broadcast DHCPREQUEST\r
+  //\r
+  if (DoUdpWrite (\r
+        Private,\r
+        &BroadcastIP,\r
+        &DhcpServerPort,\r
+        (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,\r
+        &DHCPClientPort\r
+        ) != EFI_SUCCESS) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  StatCode = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &TimeoutEvent\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    return FALSE;\r
+  }\r
+\r
+  StatCode = gBS->SetTimer (\r
+                    TimeoutEvent,\r
+                    TimerPeriodic,\r
+                    Private->Timeout * 10000000 + 1000000\r
+                    );\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return FALSE;\r
+  }\r
+  //\r
+  // wait for ACK\r
+  //\r
+  for (;;) {\r
+    if (GetOfferAck (\r
+          Private,\r
+          DHCPAckEdit,\r
+          EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP,\r
+          &ServerIp,\r
+          &DhcpServerPort,\r
+          &ClientIp,\r
+          &DHCPClientPort,\r
+          DhcpRxBuf,\r
+          TimeoutEvent\r
+          ) != EFI_SUCCESS) {\r
+      break;\r
+    }\r
+    //\r
+    // check type of response - need DHCPACK\r
+    //\r
+    if (CompareMem (\r
+          &DHCP_REQ_OPTIONS.OpReqIP.Ip,\r
+          &DhcpRxBuf->u.Dhcpv4.yiaddr,\r
+          sizeof (EFI_IPv4_ADDRESS)\r
+          ) || CompareMem (\r
+          &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip,\r
+          &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip,\r
+          sizeof (EFI_IPv4_ADDRESS)\r
+          )) {\r
+      continue;\r
+    }\r
+    //\r
+    // got ACK\r
+    // check with ARP that IP unused - good return true\r
+    //\r
+    if (!SetStationIP (Private)) {\r
+      //\r
+      // fail - send DHCPDECLINE and return false\r
+      //\r
+      DeclineOffer (Private);\r
+      break;\r
+    }\r
+\r
+    LocalPtr.OpPtr = DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1];\r
+\r
+    if (LocalPtr.OpPtr != NULL) {\r
+      CopyMem (\r
+        (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask,\r
+        &LocalPtr.SubnetMaskStr->Ip,\r
+        sizeof (EFI_IPv4_ADDRESS)\r
+        );\r
+    }\r
+\r
+    AddRouters (Private, DhcpRxBuf);\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return TRUE;\r
+  }\r
+\r
+  gBS->CloseEvent (TimeoutEvent);\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// try a DHCP server of appropriate type\r
+//\r
+STATIC\r
+BOOLEAN\r
+TryDHCPFinishDORA (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  INTN                TypeIx\r
+  )\r
+{\r
+  INTN  Index;\r
+\r
+  //\r
+  // go through the DHCP servers of the requested type\r
+  //\r
+  for (Index = 0; Index < Private->ServerCount[TypeIx]; ++Index) {\r
+    if (TryFinishDORA (Private, Index = Private->OfferCount[TypeIx][Index])) {\r
+      if (TypeIx == BINL_IX && !TryFinishBINL (Private, Index)) {\r
+        continue;\r
+      }\r
+\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// try a DHCP only server and a proxy of appropriate type\r
+//\r
+STATIC\r
+BOOLEAN\r
+TryProxyFinishDORA (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  INTN                TypeIx\r
+  )\r
+{\r
+  INTN  Index;\r
+\r
+  if (!Private->GotProxy[TypeIx]) {\r
+    //\r
+    // no proxies of the type wanted\r
+    //\r
+    return FALSE;\r
+  }\r
+  //\r
+  // go through the DHCP only servers\r
+  //\r
+  for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) {\r
+    if (TryFinishDORA (Private, Private->OfferCount[DHCP_ONLY_IX][Index])) {\r
+      if (TypeIx != BINL_IX) {\r
+        CopyProxyRxBuf (Private, Private->GotProxy[TypeIx] - 1);\r
+      } else if (!TryFinishProxyBINL (Private)) {\r
+        //\r
+        // if didn't work with this DHCP, won't work with any\r
+        //\r
+        return FALSE;\r
+      }\r
+\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// getting to the bottom of the barrel\r
+//\r
+STATIC\r
+BOOLEAN\r
+TryAnyWithBootfileFinishDORA (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  //\r
+  // try a DHCP only server who has a bootfile\r
+  //\r
+  UNION_PTR LocalPtr;\r
+  INTN      Index;\r
+\r
+  for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) {\r
+    INTN  offer;\r
+\r
+    offer = Private->OfferCount[DHCP_ONLY_IX][Index];\r
+\r
+    if (RxBuf[offer].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && TryFinishDORA (Private, offer)) {\r
+      return TRUE;\r
+    }\r
+  }\r
+  //\r
+  // really at bottom - see if be have any bootps\r
+  //\r
+  if (!Private->GotBootp) {\r
+    return FALSE;\r
+  }\r
+\r
+  DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[Private->GotBootp - 1].u.Dhcpv4.yiaddr;\r
+\r
+  if (!SetStationIP (Private)) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // treat BOOTP response as DHCP ACK packet\r
+  //\r
+  CopyParseRxBuf (Private, Private->GotBootp - 1, DHCPV4_ACK_INDEX);\r
+\r
+  LocalPtr.OpPtr = RxBuf[Private->GotBootp - 1].OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1];\r
+\r
+  if (LocalPtr.OpPtr != NULL) {\r
+    *(EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask = LocalPtr.SubnetMaskStr->Ip;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/* DoDhcpDora()\r
+ */\r
+STATIC\r
+EFI_STATUS\r
+DoDhcpDora (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  BOOLEAN             SortOffers\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_IP_FILTER Filter;\r
+  EFI_STATUS                  StatCode;\r
+  INTN                        NumOffers;\r
+\r
+  Filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP | EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;\r
+\r
+  Filter.IpCnt    = 0;\r
+  Filter.reserved = 0;\r
+\r
+  //\r
+  // set filter unicast or broadcast\r
+  //\r
+  if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {\r
+    return StatCode;\r
+  }\r
+  //\r
+  // seed random number with hardware address\r
+  //\r
+  SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress);\r
+\r
+  for (Private->Timeout = 1;\r
+       Private->Timeout < 17;\r
+       Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), Private->Timeout <<= 1\r
+      ) {\r
+    INTN  Index;\r
+\r
+    InitDhcpv4TxBuf (Private);\r
+    DHCPV4_TRANSMIT_BUFFER.xid  = Random (Private);\r
+    DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);\r
+\r
+    //\r
+    // broadcast DHCPDISCOVER\r
+    //\r
+    StatCode = DoUdpWrite (\r
+                Private,\r
+                &BroadcastIP,\r
+                &DhcpServerPort,\r
+                (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,\r
+                &DHCPClientPort\r
+                );\r
+\r
+    if (StatCode != EFI_SUCCESS) {\r
+      return StatCode;\r
+    }\r
+\r
+    CopyMem (\r
+      &Private->EfiBc.Mode->DhcpDiscover,\r
+      (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER,\r
+      sizeof (EFI_PXE_BASE_CODE_PACKET)\r
+      );\r
+\r
+    //\r
+    // get DHCPOFFER's\r
+    //\r
+    if ((StatCode = GetOffers (Private)) != EFI_SUCCESS) {\r
+      if (StatCode != EFI_NO_RESPONSE) {\r
+        return StatCode;\r
+      }\r
+\r
+      continue;\r
+    }\r
+    //\r
+    // select offer and reply DHCPREQUEST\r
+    //\r
+    if (SortOffers) {\r
+      if (TryDHCPFinishDORA(Private, PXE10_IX) || // try DHCP with PXE10\r
+        TryDHCPFinishDORA(Private, WfM11a_IX)  || // no - try with WfM\r
+        TryProxyFinishDORA(Private, PXE10_IX)  || // no - try DHCP only and proxy with PXE10\r
+        TryProxyFinishDORA(Private, WfM11a_IX) || // no - try DHCP only and proxy with WfM\r
+        TryDHCPFinishDORA(Private, BINL_IX)    || // no - try with WfM\r
+        TryProxyFinishDORA(Private, BINL_IX)   || // no - try DHCP only and proxy with PXE10\r
+        TryAnyWithBootfileFinishDORA(Private))\r
+      {\r
+        return EFI_SUCCESS;\r
+      }\r
+\r
+      continue;\r
+    }\r
+    //\r
+    // FIFO order\r
+    //\r
+    NumOffers = Private->NumOffersReceived;\r
+\r
+    for (Index = 0; Index < NumOffers; ++Index) {\r
+      //\r
+      // ignore proxies\r
+      //\r
+      if (!RxBuf[Index].u.Dhcpv4.yiaddr) {\r
+        continue;\r
+      }\r
+      //\r
+      // check if a bootp server\r
+      //\r
+      if (!RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]) {\r
+        //\r
+        // it is - just check ARP\r
+        //\r
+        if (!SetStationIP (Private)) {\r
+          continue;\r
+        }\r
+      }\r
+      //\r
+      // else check if a DHCP only server\r
+      //\r
+      else if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE))) {\r
+        //\r
+        // it is a normal DHCP offer (without any PXE options), just finish the D.O.R.A by sending DHCP request.\r
+        //\r
+        if (!TryFinishDORA (Private, Index)) {\r
+          continue;\r
+        }\r
+      } else if (TryFinishDORA (Private, Index)) {\r
+        if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) && !TryFinishBINL (Private, Index)) {\r
+          continue;\r
+        }\r
+      }\r
+\r
+      DEBUG ((DEBUG_WARN, "\nDoDhcpDora()  Got packets.  "));\r
+      return EFI_SUCCESS;\r
+    }\r
+    //\r
+    // now look for DHCP onlys and a Proxy\r
+    //\r
+    for (Index = 0; Index < NumOffers; ++Index) {\r
+      INT8  Index2;\r
+\r
+      //\r
+      // ignore proxies, bootps, non DHCP onlys, and bootable DHCPS\r
+      //\r
+      if (!RxBuf[Index].u.Dhcpv4.yiaddr ||\r
+          !RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1] ||\r
+          RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE) ||\r
+          RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]\r
+          ) {\r
+        continue;\r
+      }\r
+      //\r
+      // found non bootable DHCP only - try to find a proxy\r
+      //\r
+      for (Index2 = 0; Index2 < NumOffers; ++Index2) {\r
+        if (!RxBuf[Index2].u.Dhcpv4.yiaddr) {\r
+          if (!TryFinishDORA (Private, Index)) {\r
+            //\r
+            // DHCP no ACK\r
+            //\r
+            break;\r
+          }\r
+\r
+          if (RxBuf[Index2].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) {\r
+            CopyProxyRxBuf (Private, Index2);\r
+          } else if (!TryFinishBINL (Private, Index2)) {\r
+            continue;\r
+          }\r
+\r
+          DEBUG ((DEBUG_WARN, "\nDoDhcpDora()  Got packets.  "));\r
+          return EFI_SUCCESS;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_NO_RESPONSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// determine if the server ip is in the ip list\r
+//\r
+BOOLEAN\r
+InServerList (\r
+  EFI_IP_ADDRESS    *ServerIpPtr,\r
+  PXE_SERVER_LISTS  *ServerListPtr\r
+  )\r
+{\r
+  UINTN Index;\r
+\r
+  if (!ServerListPtr || !ServerListPtr->Ipv4List.IpCount) {\r
+    return TRUE;\r
+  }\r
+\r
+  for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) {\r
+    if (!CompareMem (\r
+          ServerIpPtr,\r
+          &ServerListPtr->Ipv4List.IpList[Index],\r
+          sizeof (EFI_IPv4_ADDRESS)\r
+          )) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+BOOLEAN\r
+ExtractBootServerList (\r
+  UINT16            Type,\r
+  DHCPV4_OP_STRUCT  *ptr,\r
+  PXE_SERVER_LISTS  **ServerListPtr\r
+  )\r
+{\r
+  UNION_PTR LocalPtr;\r
+  INTN      ServerListLen;\r
+\r
+  LocalPtr.OpPtr  = ptr;\r
+  ServerListLen   = LocalPtr.BootServersStr->Header.Length;\r
+\r
+  //\r
+  // find type\r
+  //\r
+  LocalPtr.BootServerList = LocalPtr.BootServersStr->ServerList;\r
+\r
+  while (ServerListLen) {\r
+    INTN  ServerEntryLen;\r
+\r
+    ServerEntryLen = sizeof (PXEV4_SERVER_LIST) + 2 + (LocalPtr.BootServerList->u.Ipv4List.IpCount - 1) *\r
+    sizeof (EFI_IPv4_ADDRESS);\r
+\r
+    if (NTOHS (LocalPtr.BootServerList->Type) == Type) {\r
+      *ServerListPtr = &LocalPtr.BootServerList->u;\r
+      return TRUE;\r
+    }\r
+\r
+    (LocalPtr.BytePtr) += ServerEntryLen;\r
+    ServerListLen -= ServerEntryLen;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+FreeMem (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  if (Private->TransmitBuffer != NULL) {\r
+    gBS->FreePool (Private->TransmitBuffer);\r
+    Private->TransmitBuffer = NULL;\r
+  }\r
+\r
+  if (Private->ReceiveBuffers != NULL) {\r
+    gBS->FreePool (Private->ReceiveBuffers);\r
+    Private->ReceiveBuffers = NULL;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+BOOLEAN\r
+GetMem (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  if (Private->DhcpPacketBuffer == NULL) {\r
+    Status = gBS->AllocatePool (\r
+                    EfiBootServicesData,\r
+                    sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1),\r
+                    &Private->DhcpPacketBuffer\r
+                    );\r
+\r
+    if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) {\r
+      Private->DhcpPacketBuffer = NULL;\r
+      FreeMem (Private);\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  Status = gBS->AllocatePool (\r
+                  EfiBootServicesData,\r
+                  sizeof (EFI_PXE_BASE_CODE_PACKET),\r
+                  &Private->TransmitBuffer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status) || Private->TransmitBuffer == NULL) {\r
+    gBS->FreePool (Private->DhcpPacketBuffer);\r
+    Private->DhcpPacketBuffer = NULL;\r
+    Private->TransmitBuffer   = NULL;\r
+    FreeMem (Private);\r
+    return FALSE;\r
+  }\r
+\r
+  Status = gBS->AllocatePool (\r
+                  EfiBootServicesData,\r
+                  sizeof (DHCP_RECEIVE_BUFFER) * (MAX_OFFERS),\r
+                  &Private->ReceiveBuffers\r
+                  );\r
+\r
+  if (EFI_ERROR (Status) || Private->ReceiveBuffers == NULL) {\r
+    gBS->FreePool (Private->TransmitBuffer);\r
+    gBS->FreePool (Private->DhcpPacketBuffer);\r
+    Private->DhcpPacketBuffer = NULL;\r
+    Private->TransmitBuffer   = NULL;\r
+    Private->ReceiveBuffers   = NULL;\r
+    FreeMem (Private);\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcDhcp (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+  IN BOOLEAN                    SortOffers\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_IP_FILTER Filter;\r
+  EFI_PXE_BASE_CODE_MODE      *PxebcMode;\r
+  PXE_BASECODE_DEVICE         *Private;\r
+  EFI_STATUS                  StatCode;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  Filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+  Filter.IpCnt    = 0;\r
+  Filter.reserved = 0;\r
+\r
+  DEBUG ((DEBUG_INFO, "\nBcDhcp()  Enter.  "));\r
+\r
+  PxebcMode = Private->EfiBc.Mode;\r
+\r
+  if (!GetMem (Private)) {\r
+    DEBUG ((DEBUG_ERROR, "\nBcDhcp()  GetMem() failed.\n"));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  PxebcMode->DhcpDiscoverValid = FALSE;\r
+  PxebcMode->DhcpAckReceived = FALSE;\r
+  PxebcMode->ProxyOfferReceived = FALSE;\r
+\r
+  Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP;\r
+\r
+  //\r
+  // Issue BC command\r
+  //\r
+  if (Private->TotalSeconds == 0) {\r
+    //\r
+    // put in seconds field of DHCP send packets\r
+    //\r
+    Private->TotalSeconds = 4;\r
+  }\r
+\r
+  if ((StatCode = DoDhcpDora (Private, SortOffers)) == EFI_SUCCESS) {\r
+    //\r
+    // success - copy packets\r
+    //\r
+    PxebcMode->DhcpDiscoverValid = PxebcMode->DhcpAckReceived = TRUE;\r
+\r
+    CopyMem (\r
+      &PxebcMode->DhcpAck,\r
+      (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_ACK_PACKET,\r
+      sizeof (EFI_PXE_BASE_CODE_PACKET)\r
+      );\r
+\r
+    if (PxebcMode->ProxyOfferReceived) {\r
+      CopyMem (\r
+        &PxebcMode->ProxyOffer,\r
+        (EFI_PXE_BASE_CODE_PACKET *) &PXE_OFFER_PACKET,\r
+        sizeof (EFI_PXE_BASE_CODE_PACKET)\r
+        );\r
+    }\r
+  }\r
+  //\r
+  // set filter back to unicast\r
+  //\r
+  IpFilter (Private, &Filter);\r
+\r
+  FreeMem (Private);\r
+\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  DEBUG ((DEBUG_WARN, "\nBcDhcp()  Exit = %xh  ", StatCode));\r
+\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+BOOLEAN\r
+VerifyCredentialOption (\r
+  UINT8 *tx,\r
+  UINT8 *rx\r
+  )\r
+{\r
+  UINTN n;\r
+\r
+  //\r
+  // Fail verification if either pointer is NULL.\r
+  //\r
+  if (tx == NULL || rx == NULL) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Fail verification if tx[0] is not a credential type option\r
+  // or if the length is zero or not a multiple of four.\r
+  //\r
+  if (tx[0] != VEND_PXE_CREDENTIAL_TYPES || tx[1] == 0 || tx[1] % 4 != 0) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Fail verification if rx[0] is not a credential type option\r
+  // or if the length is not equal to four.\r
+  //\r
+  if (rx[0] != VEND_PXE_CREDENTIAL_TYPES || rx[1] != 4) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Look through transmitted credential types for a copy\r
+  // of the received credential type.\r
+  //\r
+  for (n = 0; n < tx[1]; n += 4) {\r
+    if (!CompareMem (&tx[n + 2], &rx[2], 4)) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+DoDiscover (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  UINT16              OpFlags,\r
+  IN UINT16           Type,\r
+  IN UINT16           *LayerPtr,\r
+  IN BOOLEAN          UseBis,\r
+  EFI_IP_ADDRESS      *DestPtr,\r
+  PXE_SERVER_LISTS    *ServerListPtr\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT  ClientPort;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  ServerPort;\r
+  EFI_PXE_BASE_CODE_MODE      *PxebcMode;\r
+  EFI_STATUS                  StatCode;\r
+  EFI_EVENT                   TimeoutEvent;\r
+  UINT8                       OpLen;\r
+\r
+  PxebcMode = Private->EfiBc.Mode;\r
+\r
+  if (DestPtr->Addr[0] == 0) {\r
+    DEBUG ((DEBUG_WARN, "\nDoDiscover()  !DestPtr->Addr[0]"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // seed random number with hardware address\r
+  //\r
+  SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress);\r
+\r
+  if (DestPtr->Addr[0] == BroadcastIP.Addr[0]) {\r
+    ClientPort  = DHCPClientPort;\r
+    ServerPort  = DhcpServerPort;\r
+  } else {\r
+    ClientPort  = PSEUDO_DHCP_CLIENT_PORT;\r
+    ServerPort  = PseudoDhcpServerPort;\r
+  }\r
+\r
+  if (UseBis) {\r
+    *LayerPtr |= PXE_BOOT_LAYER_CREDENTIAL_FLAG;\r
+  } else {\r
+    *LayerPtr &= PXE_BOOT_LAYER_MASK;\r
+  }\r
+\r
+  for (Private->Timeout = 1;\r
+       Private->Timeout < 5;\r
+       Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Private->Timeout\r
+      ) {\r
+    InitDhcpv4TxBuf (Private);\r
+    //\r
+    // initialize DHCP message structure\r
+    //\r
+    DHCPV4_TRANSMIT_BUFFER.xid  = Random (Private);\r
+    DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds);\r
+    CopyMem (\r
+      &DHCPV4_TRANSMIT_BUFFER.ciaddr,\r
+      &PxebcMode->StationIp,\r
+      sizeof DHCPV4_TRANSMIT_BUFFER.ciaddr\r
+      );\r
+\r
+    DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type  = DHCPREQUEST;\r
+    DISCOVERoptions.Header.OpCode               = OP_VENDOR_SPECIFIC;\r
+    DISCOVERoptions.BootItem.Header.OpCode      = VEND_PXE_BOOT_ITEM;\r
+    DISCOVERoptions.BootItem.Header.Length      = DHCPV4_OPTION_LENGTH (PXE_OP_BOOT_ITEM);\r
+    DISCOVERoptions.BootItem.Type               = HTONS (Type);\r
+    DISCOVERoptions.BootItem.Layer              = HTONS (*LayerPtr);\r
+\r
+    if (UseBis) {\r
+      EFI_BIS_PROTOCOL        *BisPtr;\r
+      BIS_APPLICATION_HANDLE  BisAppHandle;\r
+      EFI_BIS_DATA            *BisDataSigInfo;\r
+      EFI_BIS_SIGNATURE_INFO  *BisSigInfo;\r
+      UINTN                   Index;\r
+      UINTN                   Index2;\r
+\r
+      BisPtr = PxebcBisStart (\r
+                Private,\r
+                &BisAppHandle,\r
+                &BisDataSigInfo\r
+                );\r
+\r
+      if (BisPtr == NULL) {\r
+        //\r
+        // %%TBD - In order to get here, BIS must have\r
+        // been present when PXEBC.Start() was called.\r
+        // BIS had to be shutdown/removed/damaged\r
+        // before PXEBC.Discover() was called.\r
+        // Do we need to document a specific error\r
+        // for this case?\r
+        //\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      //\r
+      // Compute number of credential types.\r
+      //\r
+      Index2                        = BisDataSigInfo->Length / sizeof (EFI_BIS_SIGNATURE_INFO);\r
+\r
+      DISCREDoptions.Header.OpCode  = VEND_PXE_CREDENTIAL_TYPES;\r
+\r
+      DISCREDoptions.Header.Length  = (UINT8) (Index2 * sizeof (PXE_CREDENTIAL));\r
+\r
+      OpLen = (UINT8) (DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS) + sizeof (DHCPV4_OP_HEADER) + DISCREDoptions.Header.Length);\r
+\r
+      BisSigInfo = (EFI_BIS_SIGNATURE_INFO *) BisDataSigInfo->Data;\r
+\r
+      for (Index = 0; Index < Index2; ++Index) {\r
+        UINT32  x;\r
+\r
+        CopyMem (&x, &BisSigInfo[Index], sizeof x);\r
+        x = HTONL (x);\r
+        CopyMem (&DISCREDoptions.Credentials[Index], &x, sizeof x);\r
+      }\r
+\r
+      PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo);\r
+    } else {\r
+      OpLen = DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS);\r
+    }\r
+\r
+    DISCOVERoptions.Header.Length = OpLen;\r
+\r
+    ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen - 1] = OP_END;\r
+    ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen]     = OP_END;\r
+\r
+    StatCode = DoUdpWrite (\r
+                Private,\r
+                DestPtr,\r
+                &ServerPort,\r
+                (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,\r
+                &ClientPort\r
+                );\r
+\r
+    if (StatCode != EFI_SUCCESS) {\r
+      return StatCode;\r
+    }\r
+    //\r
+    //\r
+    //\r
+    StatCode = gBS->CreateEvent (\r
+                      EVT_TIMER,\r
+                      TPL_CALLBACK,\r
+                      NULL,\r
+                      NULL,\r
+                      &TimeoutEvent\r
+                      );\r
+\r
+    if (EFI_ERROR (StatCode)) {\r
+      return StatCode;\r
+    }\r
+\r
+    StatCode = gBS->SetTimer (\r
+                      TimeoutEvent,\r
+                      TimerRelative,\r
+                      Private->Timeout * 10000000 + 1000000\r
+                      );\r
+\r
+    if (EFI_ERROR (StatCode)) {\r
+      gBS->CloseEvent (TimeoutEvent);\r
+      return StatCode;\r
+    }\r
+    //\r
+    // wait for ACK\r
+    //\r
+    for (;;) {\r
+      DHCP_RECEIVE_BUFFER *RxBufPtr;\r
+      UINT16              TmpType;\r
+      UINT16              TmpLayer;\r
+\r
+      RxBufPtr = UseBis ? &PXE_BIS_BUFFER : &PXE_ACK_BUFFER;\r
+      ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));\r
+\r
+      if (GetOfferAck (\r
+            Private,\r
+            AckEdit,\r
+            OpFlags,\r
+            (EFI_IP_ADDRESS *) &Private->ServerIp,\r
+            0,\r
+            (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr,\r
+            &ClientPort,\r
+            RxBufPtr,\r
+            TimeoutEvent\r
+            ) != EFI_SUCCESS) {\r
+        break;\r
+      }\r
+      //\r
+      // check type of response - need PXEClient DHCPACK of proper type with bootfile\r
+      //\r
+      if (!(RxBufPtr->OpAdds.Status & PXE_TYPE) ||\r
+          (UseBis && (RxBufPtr->OpAdds.Status & USE_THREE_BYTE)) ||\r
+          !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] ||\r
+          !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1] ||\r
+          !InServerList((EFI_IP_ADDRESS *)&((DHCPV4_OP_SERVER_IP *)RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX-1])->Ip, ServerListPtr)) {\r
+\r
+        continue;\r
+      }\r
+\r
+      TmpType = TmpLayer = 0;\r
+\r
+      if (RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) {\r
+        TmpType = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Type);\r
+\r
+        if (RxBufPtr->OpAdds.Status & USE_THREE_BYTE) {\r
+          TmpLayer = (UINT16) (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer >> 8);\r
+        } else {\r
+          TmpLayer = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer);\r
+        }\r
+      }\r
+\r
+      if (TmpType != Type) {\r
+        continue;\r
+      }\r
+\r
+      if (UseBis) {\r
+        if (!RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1]) {\r
+          continue;\r
+        }\r
+\r
+        if (!VerifyCredentialOption (\r
+              (UINT8 *) &DISCREDoptions.Header,\r
+              (UINT8 *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1]\r
+              )) {\r
+          continue;\r
+        }\r
+      }\r
+\r
+      *LayerPtr = TmpLayer;\r
+\r
+      if (UseBis) {\r
+        CopyMem (\r
+          &PxebcMode->PxeBisReply,\r
+          &RxBufPtr->u.Dhcpv4,\r
+          sizeof (EFI_PXE_BASE_CODE_PACKET)\r
+          );\r
+\r
+        PxebcMode->PxeBisReplyReceived = TRUE;\r
+\r
+        StatCode = DoDiscover (\r
+                    Private,\r
+                    EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,\r
+                    Type,\r
+                    LayerPtr,\r
+                    FALSE,\r
+                    &Private->ServerIp,\r
+                    0\r
+                    );\r
+\r
+        gBS->CloseEvent (TimeoutEvent);\r
+        return StatCode;\r
+      }\r
+\r
+      PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = TRUE;\r
+\r
+      CopyMem (\r
+        &PxebcMode->PxeDiscover,\r
+        &*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER,\r
+        sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER)\r
+        );\r
+\r
+      CopyMem (\r
+        &PxebcMode->PxeReply,\r
+        &*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4,\r
+        sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4)\r
+        );\r
+\r
+      AddRouters (Private, RxBufPtr);\r
+\r
+      gBS->CloseEvent (TimeoutEvent);\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    gBS->CloseEvent (TimeoutEvent);\r
+  }\r
+  //\r
+  // end for loop\r
+  //\r
+  return EFI_TIMEOUT;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+  Parameters:\r
+  Private := Pointer to PxeBc interface\r
+  Type :=\r
+  LayerPtr :=\r
+  UseBis :=\r
+  DiscoverInfoPtr :=\r
+  McastServerListPtr :=\r
+  ServerListPtr :=\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Discover (\r
+  PXE_BASECODE_DEVICE                 *Private,\r
+  IN UINT16                           Type,\r
+  IN UINT16                           *LayerPtr,\r
+  IN BOOLEAN                          UseBis,\r
+  IN EFI_PXE_BASE_CODE_DISCOVER_INFO  *DiscoverInfoPtr,\r
+  PXE_SERVER_LISTS                    *McastServerListPtr,\r
+  PXE_SERVER_LISTS                    *ServerListPtr\r
+  )\r
+{\r
+  EFI_IP_ADDRESS  DestIp;\r
+  EFI_STATUS      StatCode;\r
+\r
+  DEBUG ((DEBUG_INFO, "\nDiscover()  Type=%d  Layer=%d  ", Type, *LayerPtr));\r
+\r
+  if (UseBis) {\r
+    DEBUG ((DEBUG_INFO, "BIS  "));\r
+  }\r
+  //\r
+  // get dest IP addr - mcast, bcast, or unicast\r
+  //\r
+  if (DiscoverInfoPtr->UseMCast) {\r
+    DestIp.v4 = DiscoverInfoPtr->ServerMCastIp.v4;\r
+\r
+    DEBUG (\r
+      (DEBUG_INFO,\r
+      "\nDiscover()  MCast %d.%d.%d.%d  ",\r
+      DestIp.v4.Addr[0],\r
+      DestIp.v4.Addr[1],\r
+      DestIp.v4.Addr[2],\r
+      DestIp.v4.Addr[3])\r
+      );\r
+\r
+    if ((StatCode = DoDiscover (\r
+                      Private,\r
+                      EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,\r
+                      Type,\r
+                      LayerPtr,\r
+                      UseBis,\r
+                      &DestIp,\r
+                      McastServerListPtr\r
+                      )) != EFI_TIMEOUT) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nDiscover()  status == %r (%Xh)",\r
+        StatCode,\r
+        StatCode)\r
+        );\r
+\r
+      return StatCode;\r
+    }\r
+  }\r
+\r
+  if (DiscoverInfoPtr->UseBCast) {\r
+    DEBUG ((DEBUG_INFO, "\nDiscver()  BCast  "));\r
+\r
+    if ((StatCode = DoDiscover (\r
+                      Private,\r
+                      EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,\r
+                      Type,\r
+                      LayerPtr,\r
+                      UseBis,\r
+                      &BroadcastIP,\r
+                      McastServerListPtr\r
+                      )) != EFI_TIMEOUT) {\r
+\r
+      DEBUG ((DEBUG_WARN, "\nDiscover()  status == %r (%Xh)", StatCode, StatCode));\r
+\r
+      return StatCode;\r
+    }\r
+  }\r
+\r
+  if (DiscoverInfoPtr->UseUCast) {\r
+    UINTN Index;\r
+\r
+    DEBUG (\r
+      (DEBUG_INFO,\r
+      "\nDiscover()  UCast  IP#=%d  ",\r
+      ServerListPtr->Ipv4List.IpCount)\r
+      );\r
+\r
+    for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) {\r
+      CopyMem (&DestIp, &ServerListPtr->Ipv4List.IpList[Index], 4);\r
+\r
+      DEBUG (\r
+        (DEBUG_INFO,\r
+        "\nDiscover()  UCast %d.%d.%d.%d  ",\r
+        DestIp.v4.Addr[0],\r
+        DestIp.v4.Addr[1],\r
+        DestIp.v4.Addr[2],\r
+        DestIp.v4.Addr[3])\r
+        );\r
+\r
+      if ((StatCode = DoDiscover (\r
+                        Private,\r
+                        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,\r
+                        Type,\r
+                        LayerPtr,\r
+                        UseBis,\r
+                        &DestIp,\r
+                        0\r
+                        )) != EFI_TIMEOUT) {\r
+        DEBUG (\r
+          (DEBUG_WARN,\r
+          "\nDiscover()  status == %r (%Xh)",\r
+          StatCode,\r
+          StatCode)\r
+          );\r
+\r
+        return StatCode;\r
+      }\r
+    }\r
+  }\r
+\r
+  DEBUG ((DEBUG_WARN, "\nDiscover()  TIMEOUT"));\r
+\r
+  return EFI_TIMEOUT;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/* BcDiscover()\r
+ */\r
+\r
+/**\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcDiscover (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       * This,\r
+  IN UINT16                           Type,\r
+  IN UINT16                           *LayerPtr,\r
+  IN BOOLEAN                          UseBis,\r
+  IN EFI_PXE_BASE_CODE_DISCOVER_INFO  * DiscoverInfoPtr OPTIONAL\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;\r
+  EFI_PXE_BASE_CODE_MODE          *PxebcMode;\r
+  DHCP_RECEIVE_BUFFER             *DhcpRxBuf;\r
+  PXE_SERVER_LISTS                DefaultSrvList;\r
+  PXE_SERVER_LISTS                *ServerListPtr;\r
+  PXE_SERVER_LISTS                *McastServerListPtr;\r
+  UNION_PTR                       LocalPtr;\r
+  UINTN                           Index;\r
+  UINTN                           Index2;\r
+  BOOLEAN                         AcquiredSrvList;\r
+  EFI_STATUS                      StatCode;\r
+  PXE_BASECODE_DEVICE             *Private;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  ServerListPtr       = NULL;\r
+  McastServerListPtr  = NULL;\r
+  AcquiredSrvList     = FALSE;\r
+\r
+  PxebcMode           = Private->EfiBc.Mode;\r
+\r
+  if (!GetMem (Private)) {\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  if (UseBis) {\r
+    if (!PxebcMode->BisSupported) {\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;\r
+\r
+  if (Private->TotalSeconds == 0) {\r
+    //\r
+    // put in seconds field of DHCP send packets\r
+    //\r
+    Private->TotalSeconds = 4;\r
+  }\r
+\r
+  ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO));\r
+\r
+  //\r
+  // if layer number not zero, use previous discover\r
+  //\r
+  if (*LayerPtr != 0) {\r
+    DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0"));\r
+\r
+    if (DiscoverInfoPtr != NULL) {\r
+      DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0 && DiscoverInfoPtr != NULL\n"));\r
+\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (!PxebcMode->PxeDiscoverValid) {\r
+      DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0 && PxeDiscoverValid == 0\n"));\r
+\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (!PxebcMode->PxeReplyReceived) {\r
+      DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0 && PxeReplyReceived == 0\n"));\r
+\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (UseBis && !PxebcMode->PxeBisReplyReceived) {\r
+      DEBUG ((DEBUG_WARN, "\nBcDiscover()  layer != 0 && PxeBisReplyReceived == 0\n"));\r
+\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    DefaultInfo.UseUCast            = TRUE;\r
+    DiscoverInfoPtr                 = &DefaultInfo;\r
+\r
+    DefaultSrvList.Ipv4List.IpCount = 1;\r
+    CopyMem (&DefaultSrvList.Ipv4List.IpList[0], &Private->ServerIp, 4);\r
+\r
+    ServerListPtr = &DefaultSrvList;\r
+  }\r
+  //\r
+  // layer is zero - see if info is supplied or if we need to use info from a cached offer\r
+  //\r
+  else if (!DiscoverInfoPtr) {\r
+    //\r
+    // not supplied - generate it\r
+    // make sure that there is cached, appropriate information\r
+    // if neither DhcpAck packet nor ProxyOffer packet has pxe info, fail\r
+    //\r
+    DhcpRxBuf = (PxebcMode->ProxyOfferReceived) ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER;\r
+\r
+    if (!PxebcMode->DhcpAckReceived || !(DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE)) {\r
+      DEBUG ((DEBUG_WARN, "\nBcDiscover()  !ack && !proxy"));\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    DiscoverInfoPtr = &DefaultInfo;\r
+\r
+    LocalPtr.OpPtr  = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];\r
+\r
+    //\r
+    // if multicast enabled, need multicast address\r
+    //\r
+    if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST)) {\r
+      DefaultInfo.UseMCast = TRUE;\r
+\r
+      CopyMem (\r
+        ((EFI_IPv4_ADDRESS *) &DefaultInfo.ServerMCastIp),\r
+        &((DHCPV4_OP_IP_ADDRESS *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Ip,\r
+        sizeof (EFI_IPv4_ADDRESS)\r
+        );\r
+    }\r
+\r
+    DefaultInfo.UseBCast    = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & DISABLE_BCAST) == 0);\r
+\r
+    DefaultInfo.MustUseList = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) != 0);\r
+\r
+    DefaultInfo.UseUCast = (BOOLEAN)\r
+      (\r
+        (DefaultInfo.MustUseList) ||\r
+        ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST))\r
+      );\r
+\r
+    if ((DefaultInfo.UseUCast | DefaultInfo.MustUseList) && !ExtractBootServerList (\r
+                                                              Type,\r
+                                                              DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1],\r
+                                                              &ServerListPtr\r
+                                                              )) {\r
+      DEBUG ((DEBUG_WARN, "\nBcDiscover()  type not in list"));\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+  //\r
+  // Info supplied - make SrvList if required\r
+  // if we use ucast discovery or must use list, there better be one\r
+  //\r
+  else if (DiscoverInfoPtr->UseUCast || DiscoverInfoPtr->MustUseList) {\r
+    //\r
+    // there better be a list\r
+    //\r
+    if (DiscoverInfoPtr->IpCnt == 0) {\r
+      DEBUG ((DEBUG_WARN, "\nBcDiscover()  no bootserver list"));\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    //\r
+    // get its size\r
+    //\r
+    for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) {\r
+      if (DiscoverInfoPtr->SrvList[Index].Type == Type) {\r
+        if (DiscoverInfoPtr->SrvList[Index].AcceptAnyResponse) {\r
+          if (Index2 != 0) {\r
+            DEBUG ((DEBUG_WARN, "\nBcDiscover()  accept any?"));\r
+            EfiReleaseLock (&Private->Lock);\r
+            return EFI_INVALID_PARAMETER;\r
+          } else {\r
+            Index2                          = 1;\r
+            DefaultSrvList.Ipv4List.IpCount = 0;\r
+            ServerListPtr                   = &DefaultSrvList;\r
+            break;\r
+          }\r
+        } else {\r
+          ++Index2;\r
+        }\r
+      }\r
+    }\r
+\r
+    if (Index2 == 0) {\r
+      DEBUG ((DEBUG_WARN, "\nBcDiscover()  !Index2?"));\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (ServerListPtr == NULL) {\r
+      ServerListPtr = AllocatePool (\r
+                        sizeof (PXEV4_SERVER_LIST) + (Index2 - 1) * sizeof (EFI_IPv4_ADDRESS)\r
+                      );\r
+\r
+      if (ServerListPtr == NULL) {\r
+        EfiReleaseLock (&Private->Lock);\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      //\r
+      // build an array of IP addresses from the server list\r
+      //\r
+      AcquiredSrvList                 = TRUE;\r
+      ServerListPtr->Ipv4List.IpCount = (UINT8) Index2;\r
+\r
+      for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) {\r
+        if (DiscoverInfoPtr->SrvList[Index].Type == Type) {\r
+          CopyMem (\r
+            &ServerListPtr->Ipv4List.IpList[Index2++],\r
+            &DiscoverInfoPtr->SrvList[Index].IpAddr.v4,\r
+            sizeof ServerListPtr->Ipv4List.IpList[0]\r
+            );\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  if (DiscoverInfoPtr->MustUseList) {\r
+    McastServerListPtr = ServerListPtr;\r
+  }\r
+\r
+  if (!(DiscoverInfoPtr->UseMCast || DiscoverInfoPtr->UseBCast || DiscoverInfoPtr->UseUCast)) {\r
+    DEBUG ((DEBUG_WARN, "\nBcDiscover()  Nothing to use!\n"));\r
+\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = PxebcMode->PxeBisReplyReceived = FALSE;\r
+\r
+  StatCode = Discover (\r
+              Private,\r
+              Type,\r
+              LayerPtr,\r
+              UseBis,\r
+              DiscoverInfoPtr,\r
+              McastServerListPtr,\r
+              ServerListPtr\r
+              );\r
+\r
+  if (AcquiredSrvList) {\r
+    gBS->FreePool (ServerListPtr);\r
+  }\r
+\r
+  FreeMem (Private);\r
+\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  DEBUG (\r
+    (DEBUG_INFO,\r
+    "\nBcDiscover()  status == %r (%Xh)\n",\r
+    StatCode,\r
+    StatCode)\r
+    );\r
+\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcSetPackets (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL   * This,\r
+  BOOLEAN                         *NewDhcpDiscoverValid, OPTIONAL\r
+  BOOLEAN                         *NewDhcpAckReceived, OPTIONAL\r
+  BOOLEAN                         *NewProxyOfferReceived, OPTIONAL\r
+  BOOLEAN                         *NewPxeDiscoverValid, OPTIONAL\r
+  BOOLEAN                         *NewPxeReplyReceived, OPTIONAL\r
+  BOOLEAN                         *NewPxeBisReplyReceived, OPTIONAL\r
+  IN EFI_PXE_BASE_CODE_PACKET     * NewDhcpDiscover, OPTIONAL\r
+  IN EFI_PXE_BASE_CODE_PACKET     * NewDhcpAck, OPTIONAL\r
+  IN EFI_PXE_BASE_CODE_PACKET     * NewProxyOffer, OPTIONAL\r
+  IN EFI_PXE_BASE_CODE_PACKET     * NewPxeDiscover, OPTIONAL\r
+  IN EFI_PXE_BASE_CODE_PACKET     * NewPxeReply, OPTIONAL\r
+  IN EFI_PXE_BASE_CODE_PACKET     * NewPxeBisReply OPTIONAL\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxebcMode;\r
+  EFI_STATUS              Status;\r
+  EFI_STATUS              StatCode;\r
+  PXE_BASECODE_DEVICE     *Private;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  PxebcMode = Private->EfiBc.Mode;\r
+\r
+  if (Private->DhcpPacketBuffer == NULL) {\r
+    Status = gBS->AllocatePool (\r
+                    EfiBootServicesData,\r
+                    sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1),\r
+                    &Private->DhcpPacketBuffer\r
+                    );\r
+\r
+    if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) {\r
+      Private->DhcpPacketBuffer = NULL;\r
+      EfiReleaseLock (&Private->Lock);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+  //\r
+  // Issue BC command\r
+  //\r
+  //\r
+  // reset\r
+  //\r
+  Private->FileSize = 0;\r
+  if (NewDhcpDiscoverValid != NULL) {\r
+    PxebcMode->DhcpDiscoverValid = *NewDhcpDiscoverValid;\r
+  }\r
+\r
+  if (NewDhcpAckReceived != NULL) {\r
+    PxebcMode->DhcpAckReceived = *NewDhcpAckReceived;\r
+  }\r
+\r
+  if (NewProxyOfferReceived != NULL) {\r
+    PxebcMode->ProxyOfferReceived = *NewProxyOfferReceived;\r
+  }\r
+\r
+  if (NewPxeDiscoverValid != NULL) {\r
+    PxebcMode->PxeDiscoverValid = *NewPxeDiscoverValid;\r
+  }\r
+\r
+  if (NewPxeReplyReceived != NULL) {\r
+    PxebcMode->PxeReplyReceived = *NewPxeReplyReceived;\r
+  }\r
+\r
+  if (NewPxeBisReplyReceived != NULL) {\r
+    PxebcMode->PxeBisReplyReceived = *NewPxeBisReplyReceived;\r
+  }\r
+\r
+  if (NewDhcpDiscover != NULL) {\r
+    CopyMem (\r
+      &PxebcMode->DhcpDiscover,\r
+      NewDhcpDiscover,\r
+      sizeof *NewDhcpDiscover\r
+      );\r
+  }\r
+\r
+  if (NewDhcpAck != NULL) {\r
+    CopyParse (Private, &PxebcMode->DhcpAck, NewDhcpAck, DHCPV4_ACK_INDEX);\r
+  }\r
+\r
+  if (NewProxyOffer != NULL) {\r
+    CopyParse (Private, &PxebcMode->ProxyOffer, NewProxyOffer, PXE_OFFER_INDEX);\r
+  }\r
+\r
+  if (NewPxeDiscover != NULL) {\r
+    CopyMem (\r
+      &PxebcMode->PxeDiscover,\r
+      NewPxeDiscover,\r
+      sizeof *NewPxeDiscover\r
+      );\r
+  }\r
+\r
+  if (NewPxeReply != NULL) {\r
+    CopyParse (Private, &PxebcMode->PxeReply, NewPxeReply, PXE_ACK_INDEX);\r
+  }\r
+\r
+  if (NewPxeBisReply != NULL) {\r
+    CopyParse (Private, &PxebcMode->PxeBisReply, NewPxeBisReply, PXE_BIS_INDEX);\r
+  }\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  EfiReleaseLock (&Private->Lock);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* eof - pxe_bc_dhcp.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c
new file mode 100644 (file)
index 0000000..c5088df
--- /dev/null
@@ -0,0 +1,421 @@
+/** @file\r
+\r
+Copyright (c) 2004, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+**/\r
+\r
+\r
+#define RAND_MAX  0x10000\r
+\r
+#include "Bc.h"\r
+\r
+//\r
+// Definitions for internet group management protocol version 2 message\r
+// structure Per RFC 2236, November 1997\r
+//\r
+STATIC UINT8      RouterAlertOption[4]  = { 0x80 | 20, 4, 0, 0 };\r
+STATIC IPV4_ADDR  AllRoutersGroup       = { { 224, 0, 0, 2 } };\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+VOID\r
+ClearGroupTimer (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  UINTN               TimerId\r
+  )\r
+{\r
+  if (Private == NULL) {\r
+    return ;\r
+  }\r
+\r
+  if (TimerId >= Private->MCastGroupCount) {\r
+    return ;\r
+  }\r
+\r
+  if (Private->IgmpGroupEvent[TimerId] == NULL) {\r
+    return ;\r
+  }\r
+\r
+  gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);\r
+  Private->IgmpGroupEvent[TimerId] = NULL;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+VOID\r
+SetGroupTimer (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  UINTN               TimerId,\r
+  UINTN               MaxRespTime\r
+  )\r
+{\r
+  EFI_STATUS  EfiStatus;\r
+\r
+  if (Private == NULL) {\r
+    return ;\r
+  }\r
+\r
+  if (TimerId >= Private->MCastGroupCount) {\r
+    return ;\r
+  }\r
+\r
+  if (Private->IgmpGroupEvent[TimerId] != NULL) {\r
+    gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);\r
+  }\r
+\r
+  EfiStatus = gBS->CreateEvent (\r
+                    EVT_TIMER,\r
+                    TPL_CALLBACK,\r
+                    NULL,\r
+                    NULL,\r
+                    &Private->IgmpGroupEvent[TimerId]\r
+                    );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    Private->IgmpGroupEvent[TimerId] = NULL;\r
+    return ;\r
+  }\r
+\r
+  EfiStatus = gBS->SetTimer (\r
+                    Private->IgmpGroupEvent[TimerId],\r
+                    TimerRelative,\r
+                    MaxRespTime * 1000000 + Random (Private) % RAND_MAX\r
+                    );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);\r
+    Private->IgmpGroupEvent[TimerId] = NULL;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+VOID\r
+SendIgmpMessage (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  UINT8               Type,\r
+  INTN                GroupId\r
+  )\r
+{\r
+  Private->IgmpMessage.Type         = Type;\r
+  Private->IgmpMessage.MaxRespTime  = 0;\r
+  Private->IgmpMessage.Checksum     = 0;\r
+  Private->IgmpMessage.GroupAddress = Private->MCastGroup[GroupId];\r
+  Private->IgmpMessage.Checksum = IpChecksum (\r
+                                    (UINT16 *) &Private->IgmpMessage,\r
+                                    sizeof Private->IgmpMessage\r
+                                    );\r
+\r
+  Ipv4SendWOp (\r
+    Private,\r
+    0,\r
+    (UINT8 *) &Private->IgmpMessage,\r
+    sizeof Private->IgmpMessage,\r
+    PROT_IGMP,\r
+    RouterAlertOption,\r
+    sizeof RouterAlertOption,\r
+    ((Type == IGMP_TYPE_LEAVE_GROUP) ? AllRoutersGroup.L : Private->IgmpMessage.GroupAddress),\r
+    EFI_PXE_BASE_CODE_FUNCTION_IGMP\r
+    );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+VOID\r
+ReportIgmp (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  INTN                GroupId\r
+  )\r
+{\r
+  //\r
+  // if version 1 querier, send v1 report\r
+  //\r
+  UINT8 Type;\r
+\r
+  if (Private->Igmpv1TimeoutEvent != NULL) {\r
+    if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) {\r
+      gBS->CloseEvent (Private->Igmpv1TimeoutEvent);\r
+      Private->Igmpv1TimeoutEvent = NULL;\r
+      Private->UseIgmpv1Reporting = TRUE;\r
+    }\r
+  }\r
+\r
+  Type = (UINT8) (Private->UseIgmpv1Reporting ? IGMP_TYPE_V1REPORT : IGMP_TYPE_REPORT);\r
+\r
+  SendIgmpMessage (Private, Type, GroupId);\r
+  ClearGroupTimer (Private, GroupId);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+VOID\r
+IgmpCheckTimers (\r
+  PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  UINTN GroupId;\r
+\r
+  if (Private == NULL) {\r
+    return ;\r
+  }\r
+\r
+  for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {\r
+    if (Private->IgmpGroupEvent[GroupId] == NULL) {\r
+      continue;\r
+    }\r
+\r
+    if (!EFI_ERROR (gBS->CheckEvent (Private->IgmpGroupEvent[GroupId]))) {\r
+      //\r
+      // send a report\r
+      //\r
+      ReportIgmp (Private, GroupId);\r
+    }\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return 0 := Group not found\r
+  @return other := Group ID#\r
+\r
+**/\r
+STATIC\r
+INTN\r
+FindMulticastGroup (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  UINT32              GroupAddress\r
+  )\r
+{\r
+  UINTN GroupId;\r
+\r
+  for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {\r
+    if (Private->MCastGroup[GroupId] == GroupAddress) {\r
+      return GroupId + 1;\r
+    }\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+VOID\r
+IgmpJoinGroup (\r
+  PXE_BASECODE_DEVICE  *Private,\r
+  EFI_IP_ADDRESS       *GroupPtr\r
+  )\r
+{\r
+  UINT32  Grp;\r
+\r
+  Grp = *(UINT32 *) GroupPtr;\r
+\r
+  //\r
+  // see if we already have it or if we can't take anymore\r
+  //\r
+  if (FindMulticastGroup (Private, Grp) || Private->MCastGroupCount == MAX_MCAST_GROUPS) {\r
+    return ;\r
+  }\r
+  //\r
+  // add the group\r
+  //\r
+  Private->MCastGroup[Private->MCastGroupCount] = Grp;\r
+\r
+  ReportIgmp (Private, Private->MCastGroupCount);\r
+  //\r
+  // send a report\r
+  // so it will get sent again per RFC 2236\r
+  //\r
+  SetGroupTimer (\r
+    Private,\r
+    Private->MCastGroupCount++,\r
+    UNSOLICITED_REPORT_INTERVAL * 10\r
+    );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+VOID\r
+IgmpLeaveGroup (\r
+  PXE_BASECODE_DEVICE       *Private,\r
+  EFI_IP_ADDRESS            *GroupPtr\r
+  )\r
+{\r
+  UINT32  Grp;\r
+  UINTN   GroupId;\r
+\r
+  Grp = *(UINT32 *) GroupPtr;\r
+\r
+  //\r
+  // if not in group, ignore\r
+  //\r
+  GroupId = FindMulticastGroup (Private, Grp);\r
+\r
+  if (GroupId == 0) {\r
+    return ;\r
+  }\r
+  //\r
+  // if not v1 querrier, send leave group IGMP message\r
+  //\r
+  if (Private->Igmpv1TimeoutEvent != NULL) {\r
+    if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) {\r
+      gBS->CloseEvent (Private->Igmpv1TimeoutEvent);\r
+      Private->Igmpv1TimeoutEvent = NULL;\r
+      Private->UseIgmpv1Reporting = TRUE;\r
+    } else {\r
+      SendIgmpMessage (Private, IGMP_TYPE_LEAVE_GROUP, GroupId - 1);\r
+    }\r
+  }\r
+\r
+  while (GroupId < Private->MCastGroupCount) {\r
+    Private->MCastGroup[GroupId - 1]      = Private->MCastGroup[GroupId];\r
+    Private->IgmpGroupEvent[GroupId - 1]  = Private->IgmpGroupEvent[GroupId];\r
+    ++GroupId;\r
+  }\r
+\r
+  --Private->MCastGroupCount;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+VOID\r
+HandleIgmp (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  IGMPV2_MESSAGE      *IgmpMessagePtr,\r
+  UINTN               IgmpLength\r
+  )\r
+{\r
+  EFI_STATUS  EfiStatus;\r
+  UINTN       GroupId;\r
+  INTN        MaxRespTime;\r
+\r
+  if (Private == NULL) {\r
+    return ;\r
+  }\r
+\r
+  if (Private->MCastGroupCount == 0) {\r
+    //\r
+    // if we don't belong to any multicast groups, ignore\r
+    //\r
+    return ;\r
+  }\r
+  //\r
+  // verify checksum\r
+  //\r
+  if (IpChecksum ((UINT16 *) IgmpMessagePtr, IgmpLength)) {\r
+    //\r
+    // bad checksum - ignore packet\r
+    //\r
+    return ;\r
+  }\r
+\r
+  switch (IgmpMessagePtr->Type) {\r
+  case IGMP_TYPE_QUERY:\r
+    //\r
+    // if a version 1 querier, note the fact and set max resp time\r
+    //\r
+    MaxRespTime = IgmpMessagePtr->MaxRespTime;\r
+\r
+    if (MaxRespTime == 0) {\r
+      Private->UseIgmpv1Reporting = TRUE;\r
+\r
+      if (Private->Igmpv1TimeoutEvent != NULL) {\r
+        gBS->CloseEvent (Private->Igmpv1TimeoutEvent);\r
+      }\r
+\r
+      EfiStatus = gBS->CreateEvent (\r
+                        EVT_TIMER,\r
+                        TPL_CALLBACK,\r
+                        NULL,\r
+                        NULL,\r
+                        &Private->Igmpv1TimeoutEvent\r
+                        );\r
+\r
+      if (EFI_ERROR (EfiStatus)) {\r
+        Private->Igmpv1TimeoutEvent = NULL;\r
+      } else {\r
+        EfiStatus = gBS->SetTimer (\r
+                          Private->Igmpv1TimeoutEvent,\r
+                          TimerRelative,\r
+                          (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000\r
+                          );\r
+      }\r
+\r
+      MaxRespTime = IGMP_DEFAULT_MAX_RESPONSE_TIME * 10;\r
+    }\r
+    //\r
+    // if a general query (!GroupAddress), set all our group timers\r
+    //\r
+    if (!IgmpMessagePtr->GroupAddress) {\r
+      for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {\r
+        SetGroupTimer (Private, GroupId, MaxRespTime);\r
+      }\r
+    } else {\r
+      //\r
+      // specific query - set only specific group\r
+      //\r
+      GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress);\r
+\r
+      if (GroupId != 0) {\r
+        SetGroupTimer (Private, GroupId - 1, MaxRespTime);\r
+      }\r
+    }\r
+\r
+    break;\r
+\r
+  //\r
+  // if we have a timer running for this group, clear it\r
+  //\r
+  case IGMP_TYPE_V1REPORT:\r
+  case IGMP_TYPE_REPORT:\r
+    GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress);\r
+\r
+    if (GroupId != 0) {\r
+      ClearGroupTimer (Private, GroupId - 1);\r
+    }\r
+\r
+    break;\r
+  }\r
+}\r
+\r
+/* EOF - pxe_bc_igmp.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c
new file mode 100644 (file)
index 0000000..6330679
--- /dev/null
@@ -0,0 +1,846 @@
+/** @file\r
+\r
+Copyright (c) 2004, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  pxe_bc_ip.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Bc.h"\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+  Check if two IP addresses are on the same subnet.\r
+\r
+  @param  IpLength     Length of IP address in bytes.\r
+  @param  Ip1          IP address to check.\r
+  @param  Ip2          IP address to check.\r
+  @param  SubnetMask   Subnet mask to check with.\r
+\r
+  @retval TRUE         IP addresses are on the same subnet.\r
+  @retval FALSE        IP addresses are on different subnets.\r
+\r
+**/\r
+BOOLEAN\r
+OnSameSubnet (\r
+  IN UINTN           IpLength,\r
+  IN EFI_IP_ADDRESS  *Ip1,\r
+  IN EFI_IP_ADDRESS  *Ip2,\r
+  IN EFI_IP_ADDRESS  *SubnetMask\r
+  )\r
+{\r
+  if (IpLength == 0 || Ip1 == NULL || Ip2 == NULL || SubnetMask == NULL) {\r
+    return FALSE;\r
+  }\r
+\r
+  while (IpLength-- != 0) {\r
+    if ((Ip1->v6.Addr[IpLength] ^ Ip2->v6.Addr[IpLength]) & SubnetMask->v6.Addr[IpLength]) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+  Add router to router table.\r
+\r
+  @param  Private      Pointer PxeBc instance data.\r
+  @param  RouterIpPtr  Pointer to router IP address.\r
+\r
+  @return Nothing\r
+\r
+**/\r
+VOID\r
+IpAddRouter (\r
+  IN PXE_BASECODE_DEVICE *Private,\r
+  IN EFI_IP_ADDRESS      *RouterIpPtr\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  UINTN                   Index;\r
+\r
+  if (Private == NULL || RouterIpPtr == NULL) {\r
+    return ;\r
+  }\r
+\r
+  PxeBcMode = Private->EfiBc.Mode;\r
+\r
+  //\r
+  // if we are filled up or this is not on the same subnet, forget it\r
+  //\r
+  if ((PxeBcMode->RouteTableEntries == PXE_ROUTER_TABLE_SIZE) ||\r
+    !OnSameSubnet(Private->IpLength, &PxeBcMode->StationIp, RouterIpPtr, &PxeBcMode->SubnetMask)) {\r
+    return ;\r
+  }\r
+  //\r
+  // make sure we don't already have it\r
+  //\r
+  for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {\r
+    if (!CompareMem (\r
+          &PxeBcMode->RouteTable[Index].GwAddr,\r
+          RouterIpPtr,\r
+          Private->IpLength\r
+          )) {\r
+      return ;\r
+    }\r
+  }\r
+  //\r
+  // keep it\r
+  //\r
+  ZeroMem (\r
+    &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries],\r
+    sizeof (EFI_PXE_BASE_CODE_ROUTE_ENTRY)\r
+    );\r
+\r
+  CopyMem (\r
+    &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries++].GwAddr,\r
+    RouterIpPtr,\r
+    Private->IpLength\r
+    );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// return router ip to use for DestIp (0 if none)\r
+//\r
+STATIC\r
+EFI_IP_ADDRESS *\r
+GetRouterIp (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  EFI_IP_ADDRESS      *DestIpPtr\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  UINTN                   Index;\r
+\r
+  if (Private == NULL || DestIpPtr == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  PxeBcMode = Private->EfiBc.Mode;\r
+\r
+  for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {\r
+    if (OnSameSubnet (\r
+          Private->IpLength,\r
+          &PxeBcMode->RouteTable[Index].IpAddr,\r
+          DestIpPtr,\r
+          &PxeBcMode->RouteTable[Index].SubnetMask\r
+          )) {\r
+      return &PxeBcMode->RouteTable[Index].GwAddr;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// routine to send ipv4 packet\r
+// ipv4 header of length HdrLth in TransmitBufferPtr\r
+// routine fills in ipv4hdr Ver_Hdl, TotalLength, and Checksum, moves in Data\r
+// and gets dest MAC address\r
+//\r
+#define IP_TX_BUFFER  ((IPV4_BUFFER *) Private->TransmitBufferPtr)\r
+#define IP_TX_HEADER  IP_TX_BUFFER->IpHeader\r
+\r
+EFI_STATUS\r
+Ipv4Xmt (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  UINT32                      GatewayIp,\r
+  UINTN                       IpHeaderLength,\r
+  UINTN                       TotalHeaderLength,\r
+  VOID                        *Data,\r
+  UINTN                       DataLength,\r
+  EFI_PXE_BASE_CODE_FUNCTION  Function\r
+  )\r
+{\r
+  EFI_MAC_ADDRESS             DestMac;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+  EFI_PXE_BASE_CODE_MODE      *PxeBcMode;\r
+  EFI_STATUS                  StatCode;\r
+  UINTN                       PacketLength;\r
+\r
+  Snp           = Private->SimpleNetwork;\r
+  PxeBcMode     = Private->EfiBc.Mode;\r
+  StatCode      = EFI_SUCCESS;\r
+  PacketLength  = TotalHeaderLength + DataLength;\r
+\r
+  //\r
+  // get dest MAC address\r
+  // multicast - convert to hw equiv\r
+  // unicast on same net, use arp\r
+  // on different net, arp for router\r
+  //\r
+  if (IP_TX_HEADER.DestAddr.L == BROADCAST_IPv4) {\r
+    CopyMem (&DestMac, &Snp->Mode->BroadcastAddress, sizeof (DestMac));\r
+  } else if (IS_MULTICAST (&IP_TX_HEADER.DestAddr)) {\r
+    StatCode = (*Snp->MCastIpToMac) (Snp, PxeBcMode->UsingIpv6, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr, &DestMac);\r
+  } else {\r
+    UINT32  Ip;\r
+\r
+    if (OnSameSubnet (\r
+          Private->IpLength,\r
+          &PxeBcMode->StationIp,\r
+          (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr,\r
+          &PxeBcMode->SubnetMask\r
+          )) {\r
+      Ip = IP_TX_HEADER.DestAddr.L;\r
+    } else if (GatewayIp != 0) {\r
+      Ip = GatewayIp;\r
+    } else {\r
+      EFI_IP_ADDRESS  *TmpIp;\r
+\r
+      TmpIp = GetRouterIp (Private, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr);\r
+\r
+      if (TmpIp == NULL) {\r
+        DEBUG (\r
+          (DEBUG_WARN,\r
+          "\nIpv4Xmit()  Exit #1  %xh (%r)",\r
+          EFI_NO_RESPONSE,\r
+          EFI_NO_RESPONSE)\r
+          );\r
+\r
+        return EFI_NO_RESPONSE;\r
+        //\r
+        // no router\r
+        //\r
+      }\r
+\r
+      Ip = TmpIp->Addr[0];\r
+    }\r
+\r
+    if (!GetHwAddr (\r
+          Private,\r
+          (EFI_IP_ADDRESS *) &Ip,\r
+          (EFI_MAC_ADDRESS *) &DestMac\r
+          )) {\r
+      if (!PxeBcMode->AutoArp) {\r
+        DEBUG (\r
+          (DEBUG_WARN,\r
+          "\nIpv4Xmit()  Exit #2  %xh (%r)",\r
+          EFI_DEVICE_ERROR,\r
+          EFI_DEVICE_ERROR)\r
+          );\r
+\r
+        return EFI_DEVICE_ERROR;\r
+      } else {\r
+        StatCode = DoArp (\r
+                    Private,\r
+                    (EFI_IP_ADDRESS *) &Ip,\r
+                    (EFI_MAC_ADDRESS *) &DestMac\r
+                    );\r
+      }\r
+    }\r
+  }\r
+\r
+  if (EFI_ERROR (StatCode)) {\r
+    DEBUG ((DEBUG_WARN, "\nIpv4Xmit()  Exit #3  %xh (%r)", StatCode, StatCode));\r
+    return StatCode;\r
+  }\r
+  //\r
+  // fill in packet info\r
+  //\r
+  SET_IPV4_VER_HDL (&IP_TX_HEADER, IpHeaderLength);\r
+  IP_TX_HEADER.TotalLength    = HTONS (PacketLength);\r
+  IP_TX_HEADER.HeaderChecksum = IpChecksum ((UINT16 *) &IP_TX_HEADER, IpHeaderLength);\r
+  CopyMem (((UINT8 *) &IP_TX_HEADER) + TotalHeaderLength, Data, DataLength);\r
+\r
+  //\r
+  // send it\r
+  //\r
+  return SendPacket (\r
+          Private,\r
+          (UINT8 *) &IP_TX_HEADER - Snp->Mode->MediaHeaderSize,\r
+          &IP_TX_HEADER,\r
+          PacketLength,\r
+          &DestMac,\r
+          PXE_PROTOCOL_ETHERNET_IP,\r
+          Function\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// send ipv4 packet with option\r
+//\r
+EFI_STATUS\r
+Ipv4SendWOp (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  UINT32                      GatewayIp,\r
+  UINT8                       *Msg,\r
+  UINTN                       MessageLength,\r
+  UINT8                       Prot,\r
+  UINT8                       *Option,\r
+  UINTN                       OptionLength,\r
+  UINT32                      DestIp,\r
+  EFI_PXE_BASE_CODE_FUNCTION  Function\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  UINTN                   HdrLth;\r
+\r
+  PxeBcMode = Private->EfiBc.Mode;\r
+  HdrLth    = sizeof (IPV4_HEADER) + OptionLength;\r
+\r
+  ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER));\r
+  IP_TX_HEADER.TimeToLive     = PxeBcMode->TTL;\r
+  IP_TX_HEADER.TypeOfService  = PxeBcMode->ToS;\r
+  IP_TX_HEADER.Protocol       = Prot;\r
+  IP_TX_HEADER.SrcAddr.L      = *(UINT32 *) &PxeBcMode->StationIp;\r
+  IP_TX_HEADER.DestAddr.L     = DestIp;\r
+  IP_TX_HEADER.Id             = Random (Private);\r
+  CopyMem (IP_TX_BUFFER->u.Data, Option, OptionLength);\r
+  return Ipv4Xmt (\r
+          Private,\r
+          GatewayIp,\r
+          HdrLth,\r
+          HdrLth,\r
+          Msg,\r
+          MessageLength,\r
+          Function\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// send MessageLength message at MessagePtr - higher level protocol header already in TransmitBufferPtr, length HdrSize\r
+//\r
+EFI_STATUS\r
+Ip4Send (\r
+  PXE_BASECODE_DEVICE                          *Private,  // pointer to instance data\r
+  UINTN               MayFrag,                            //\r
+  UINT8                                    Prot,          // protocol\r
+  UINT32                          SrcIp,                  // Source IP address\r
+  UINT32                 DestIp,                          // Destination IP address\r
+  UINT32              GatewayIp,                          // used if not NULL and needed\r
+  UINTN               HdrSize,                            // protocol header byte length\r
+  UINT8               *MessagePtr,                        // pointer to data\r
+  UINTN               MessageLength                       // data byte length\r
+  )\r
+{\r
+  EFI_STATUS  StatCode;\r
+  UINTN       TotDataLength;\r
+\r
+  TotDataLength = HdrSize + MessageLength;\r
+\r
+  if (TotDataLength > MAX_IPV4_DATA_SIZE) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nIp4Send()  Exit #1  %xh (%r)",\r
+      EFI_BAD_BUFFER_SIZE,\r
+      EFI_BAD_BUFFER_SIZE)\r
+      );\r
+\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER));\r
+  IP_TX_HEADER.TimeToLive = DEFAULT_TTL;\r
+  IP_TX_HEADER.Protocol   = Prot;\r
+  IP_TX_HEADER.SrcAddr.L  = SrcIp;\r
+  IP_TX_HEADER.DestAddr.L = DestIp;\r
+  IP_TX_HEADER.Id         = Random (Private);\r
+\r
+  if (!MayFrag) {\r
+    *(UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_NO_FRAG >> 8;\r
+  }\r
+  //\r
+  // check for need to fragment\r
+  //\r
+  if (TotDataLength > MAX_IPV4_FRAME_DATA_SIZE) {\r
+    UINTN   DataLengthSent;\r
+    UINT16  FragmentOffset;\r
+\r
+    FragmentOffset = IP_MORE_FRAG;\r
+    //\r
+    // frag offset field\r
+    //\r
+    if (!MayFrag) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nIp4Send()  Exit #2  %xh (%r)",\r
+        EFI_BAD_BUFFER_SIZE,\r
+        EFI_BAD_BUFFER_SIZE)\r
+        );\r
+\r
+      return EFI_BAD_BUFFER_SIZE;\r
+    }\r
+    //\r
+    // send out in fragments - first includes upper level header\r
+    // all are max and include more frag bit except last\r
+    //\r
+    * (UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_MORE_FRAG >> 8;\r
+\r
+#define IPV4_FRAG_SIZE    (MAX_IPV4_FRAME_DATA_SIZE & 0xfff8)\r
+#define IPV4_FRAG_OFF_INC (IPV4_FRAG_SIZE >> 3)\r
+\r
+    DataLengthSent = IPV4_FRAG_SIZE - HdrSize;\r
+\r
+    StatCode = Ipv4Xmt (\r
+                Private,\r
+                GatewayIp,\r
+                sizeof (IPV4_HEADER),\r
+                sizeof (IPV4_HEADER) + HdrSize,\r
+                MessagePtr,\r
+                DataLengthSent,\r
+                Private->Function\r
+                );\r
+\r
+    if (EFI_ERROR (StatCode)) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nIp4Send()  Exit #3  %xh (%r)",\r
+        StatCode,\r
+        StatCode)\r
+        );\r
+\r
+      return StatCode;\r
+    }\r
+\r
+    MessagePtr += DataLengthSent;\r
+    MessageLength -= DataLengthSent;\r
+    FragmentOffset += IPV4_FRAG_OFF_INC;\r
+    IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset);\r
+\r
+    while (MessageLength > IPV4_FRAG_SIZE) {\r
+      StatCode = Ipv4Xmt (\r
+                  Private,\r
+                  GatewayIp,\r
+                  sizeof (IPV4_HEADER),\r
+                  sizeof (IPV4_HEADER),\r
+                  MessagePtr,\r
+                  IPV4_FRAG_SIZE,\r
+                  Private->Function\r
+                  );\r
+\r
+      if (EFI_ERROR (StatCode)) {\r
+        DEBUG (\r
+          (DEBUG_WARN,\r
+          "\nIp4Send()  Exit #3  %xh (%r)",\r
+          StatCode,\r
+          StatCode)\r
+          );\r
+\r
+        return StatCode;\r
+      }\r
+\r
+      MessagePtr += IPV4_FRAG_SIZE;\r
+      MessageLength -= IPV4_FRAG_SIZE;\r
+      FragmentOffset += IPV4_FRAG_OFF_INC;\r
+      IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset);\r
+    }\r
+\r
+    * (UINT8 *) (&IP_TX_HEADER.FragmentFields) &= ~(IP_MORE_FRAG >> 8);\r
+    HdrSize = 0;\r
+  }\r
+  //\r
+  // transmit\r
+  //\r
+  return Ipv4Xmt (\r
+          Private,\r
+          GatewayIp,\r
+          sizeof (IPV4_HEADER),\r
+          sizeof (IPV4_HEADER) + HdrSize,\r
+          MessagePtr,\r
+          MessageLength,\r
+          Private->Function\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// return true if dst IP in receive header matched with what's enabled\r
+//\r
+STATIC\r
+BOOLEAN\r
+IPgood (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  IPV4_HEADER         *IpHeader\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  UINTN                   Index;\r
+\r
+  PxeBcMode = Private->EfiBc.Mode;\r
+\r
+  if (PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) {\r
+    return TRUE;\r
+  }\r
+\r
+  if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) &&\r
+      IS_MULTICAST (&IpHeader->DestAddr)\r
+        ) {\r
+    return TRUE;\r
+  }\r
+\r
+  if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) &&\r
+      PxeBcMode->StationIp.Addr[0] == IpHeader->DestAddr.L\r
+      ) {\r
+    return TRUE;\r
+  }\r
+\r
+  if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) && IpHeader->DestAddr.L == BROADCAST_IPv4) {\r
+    return TRUE;\r
+  }\r
+\r
+  for (Index = 0; Index < PxeBcMode->IpFilter.IpCnt; ++Index) {\r
+    if (IpHeader->DestAddr.L == PxeBcMode->IpFilter.IpList[Index].Addr[0]) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// receive up to MessageLength message into MessagePtr for protocol Prot\r
+// return message length, src/dest ips if select any, and pointer to protocol\r
+// header routine will filter based on source and/or dest ip if OpFlags set.\r
+//\r
+EFI_STATUS\r
+IpReceive (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  PXE_OPFLAGS         OpFlags,\r
+  EFI_IP_ADDRESS      *SrcIpPtr,\r
+  EFI_IP_ADDRESS      *DestIpPtr,\r
+  UINT8               Prot,\r
+  VOID                *HeaderPtr,\r
+  UINTN               HdrSize,\r
+  UINT8               *MessagePtr,\r
+  UINTN               *MessageLengthPtr,\r
+  EFI_EVENT           TimeoutEvent\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  EFI_STATUS              StatCode;\r
+  UINTN                   ByteCount;\r
+  UINTN                   FragmentCount;\r
+  UINTN                   ExpectedPacketLength;\r
+  UINTN                   Id;\r
+  BOOLEAN                 GotFirstFragment;\r
+  BOOLEAN                 GotLastFragment;\r
+\r
+  DEBUG (\r
+    (DEBUG_NET,\r
+    "\nIpReceive()  Hdr=%Xh  HdrSz=%d  Data=%Xh  DataSz=%d",\r
+    HeaderPtr,\r
+    HdrSize,\r
+    MessagePtr,\r
+    *MessageLengthPtr)\r
+    );\r
+\r
+  PxeBcMode                     = Private->EfiBc.Mode;\r
+  PxeBcMode->IcmpErrorReceived  = FALSE;\r
+\r
+  ExpectedPacketLength          = 0;\r
+  GotFirstFragment              = FALSE;\r
+  GotLastFragment               = FALSE;\r
+  FragmentCount                 = 0;\r
+  ByteCount                     = 0;\r
+  Id = 0;\r
+\r
+  for (;;) {\r
+    IPV4_HEADER IpHdr;\r
+    UINTN       FFlds;\r
+    UINTN       TotalLength;\r
+    UINTN       FragmentOffset;\r
+    UINTN       HeaderSize;\r
+    UINTN       BufferSize;\r
+    UINTN       IpHeaderLength;\r
+    UINTN       DataLength;\r
+    UINT16      Protocol;\r
+    UINT8       *NextHdrPtr;\r
+    UINT8       *PacketPtr;\r
+\r
+    StatCode = WaitForReceive (\r
+                Private,\r
+                Private->Function,\r
+                TimeoutEvent,\r
+                &HeaderSize,\r
+                &BufferSize,\r
+                &Protocol\r
+                );\r
+\r
+    if (EFI_ERROR (StatCode)) {\r
+      return StatCode;\r
+    }\r
+\r
+    PacketPtr = Private->ReceiveBufferPtr + HeaderSize;\r
+\r
+    if (Protocol == PXE_PROTOCOL_ETHERNET_ARP) {\r
+      HandleArpReceive (\r
+        Private,\r
+        (ARP_PACKET *) PacketPtr,\r
+        Private->ReceiveBufferPtr\r
+        );\r
+\r
+      continue;\r
+    }\r
+\r
+    if (Protocol != PXE_PROTOCOL_ETHERNET_IP) {\r
+      continue;\r
+    }\r
+\r
+#define IpRxHeader  ((IPV4_HEADER *) PacketPtr)\r
+\r
+    //\r
+    // filter for version & check sum\r
+    //\r
+    IpHeaderLength = IPV4_HEADER_LENGTH (IpRxHeader);\r
+\r
+    if ((IpRxHeader->VersionIhl >> 4) != IPVER4) {\r
+      continue;\r
+    }\r
+\r
+    if (IpChecksum ((UINT16 *) IpRxHeader, IpHeaderLength)) {\r
+      continue;\r
+    }\r
+\r
+    CopyMem (&IpHdr, IpRxHeader, sizeof (IpHdr));\r
+    TotalLength = NTOHS (IpHdr.TotalLength);\r
+\r
+    if (IpHdr.Protocol == PROT_TCP) {\r
+      //\r
+      // The NextHdrPtr is used to seed the header buffer we are passing back.\r
+      // That being the case, we want to see everything in pPkt which contains\r
+      // everything but the ethernet (or whatever) frame.  IP + TCP in this case.\r
+      //\r
+      DataLength  = TotalLength;\r
+      NextHdrPtr  = PacketPtr;\r
+    } else {\r
+      DataLength  = TotalLength - IpHeaderLength;\r
+      NextHdrPtr  = PacketPtr + IpHeaderLength;\r
+    }\r
+    //\r
+    // If this is an ICMP, it might not be for us.\r
+    // Double check the state of the IP stack and the\r
+    // packet fields before assuming it is an ICMP\r
+    // error.  ICMP requests are not supported by the\r
+    // PxeBc IP stack and should be ignored.\r
+    //\r
+    if (IpHdr.Protocol == PROT_ICMP) {\r
+      ICMPV4_HEADER *Icmpv4;\r
+\r
+      Icmpv4 = (ICMPV4_HEADER *) NextHdrPtr;\r
+\r
+      //\r
+      // For now only obvious ICMP error replies will be accepted by\r
+      // this stack.  This still makes us vulnerable to DoS attacks.\r
+      // But at least we will not be killed by DHCP daemons.\r
+      //\r
+      switch (Icmpv4->Type) {\r
+      case ICMP_REDIRECT:\r
+      case ICMP_ECHO:\r
+      case ICMP_ROUTER_ADV:\r
+      case ICMP_ROUTER_SOLICIT:\r
+      case ICMP_TIMESTAMP:\r
+      case ICMP_TIMESTAMP_REPLY:\r
+      case ICMP_INFO_REQ:\r
+      case ICMP_INFO_REQ_REPLY:\r
+      case ICMP_SUBNET_MASK_REQ:\r
+      case ICMP_SUBNET_MASK_REPLY:\r
+      default:\r
+        continue;\r
+\r
+      //\r
+      // %%TBD - This should be implemented.\r
+      //\r
+      case ICMP_ECHO_REPLY:\r
+        continue;\r
+\r
+      case ICMP_DEST_UNREACHABLE:\r
+      case ICMP_TIME_EXCEEDED:\r
+      case ICMP_PARAMETER_PROBLEM:\r
+      case ICMP_SOURCE_QUENCH:\r
+        PxeBcMode->IcmpErrorReceived = TRUE;\r
+\r
+        CopyMem (\r
+          &PxeBcMode->IcmpError,\r
+          NextHdrPtr,\r
+          sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)\r
+          );\r
+\r
+        DEBUG (\r
+          (DEBUG_NET,\r
+          "\nIpReceive()  Exit #1  %Xh (%r)",\r
+          EFI_ICMP_ERROR,\r
+          EFI_ICMP_ERROR)\r
+          );\r
+      }\r
+\r
+      return EFI_ICMP_ERROR;\r
+    }\r
+\r
+    if (IpHdr.Protocol == PROT_IGMP) {\r
+      HandleIgmp (Private, (IGMPV2_MESSAGE *) NextHdrPtr, DataLength);\r
+\r
+      DEBUG ((DEBUG_NET, "\n  IGMP"));\r
+      continue;\r
+    }\r
+    //\r
+    // check for protocol\r
+    //\r
+    if (IpHdr.Protocol != Prot) {\r
+      continue;\r
+    }\r
+    //\r
+    // do filtering\r
+    //\r
+    if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr && SrcIpPtr->Addr[0] != IpHdr.SrcAddr.L) {\r
+      DEBUG ((DEBUG_NET, "\n  Not expected source IP address."));\r
+      continue;\r
+    }\r
+\r
+    if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) {\r
+      if (!IPgood (Private, &IpHdr)) {\r
+        continue;\r
+      }\r
+    } else if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP)) {\r
+      if (DestIpPtr == NULL) {\r
+        if (PxeBcMode->StationIp.Addr[0] != IpHdr.DestAddr.L) {\r
+          continue;\r
+        }\r
+      } else if (DestIpPtr->Addr[0] != IpHdr.DestAddr.L) {\r
+        continue;\r
+      }\r
+    }\r
+    //\r
+    // get some data we need\r
+    //\r
+    FFlds           = NTOHS (IpHdr.FragmentFields);\r
+    FragmentOffset  = ((FFlds & IP_FRAG_OFF_MSK) << 3);\r
+\r
+    /* Keep count of fragments that belong to this session.\r
+     * If we get packets with a different IP ID number,\r
+     * ignore them.  Ignored packets should be handled\r
+     * by the upper level protocol.\r
+     */\r
+    if (FragmentCount == 0) {\r
+      Id = IpHdr.Id;\r
+\r
+      if (DestIpPtr != NULL) {\r
+        DestIpPtr->Addr[0] = IpHdr.DestAddr.L;\r
+      }\r
+\r
+      if (SrcIpPtr != NULL) {\r
+        SrcIpPtr->Addr[0] = IpHdr.SrcAddr.L;\r
+      }\r
+    } else {\r
+      if (IpHdr.Id != Id) {\r
+        continue;\r
+      }\r
+    }\r
+\r
+    ++FragmentCount;\r
+\r
+    /* Fragment management.\r
+     */\r
+    if (FragmentOffset == 0) {\r
+      /* This is the first fragment (may also be the\r
+       * only fragment).\r
+       */\r
+      GotFirstFragment = TRUE;\r
+\r
+      /* If there is a separate protocol header buffer,\r
+       * copy the header, adjust the data pointer and\r
+       * the data length.\r
+       */\r
+      if (HdrSize != 0) {\r
+        CopyMem (HeaderPtr, NextHdrPtr, HdrSize);\r
+\r
+        NextHdrPtr += HdrSize;\r
+        DataLength -= HdrSize;\r
+      }\r
+    } else {\r
+      /* If there is a separate protocol header buffer,\r
+       * adjust the fragment offset.\r
+       */\r
+      FragmentOffset -= HdrSize;\r
+    }\r
+\r
+    /* See if this is the last fragment.\r
+     */\r
+    if (!(FFlds & IP_MORE_FRAG)) {\r
+      //\r
+      // This is the last fragment (may also be the only fragment).\r
+      //\r
+      GotLastFragment = TRUE;\r
+\r
+      /* Compute the expected length of the assembled\r
+       * packet.  This will be used to decide if we\r
+       * have gotten all of the fragments.\r
+       */\r
+      ExpectedPacketLength = FragmentOffset + DataLength;\r
+    }\r
+\r
+    DEBUG (\r
+      (DEBUG_NET,\r
+      "\n  ID = %Xh  Off = %d  Len = %d",\r
+      Id,\r
+      FragmentOffset,\r
+      DataLength)\r
+      );\r
+\r
+    /* Check for receive buffer overflow.\r
+     */\r
+    if (FragmentOffset + DataLength > *MessageLengthPtr) {\r
+      /* There is not enough space in the receive\r
+       * buffer for the fragment.\r
+       */\r
+      DEBUG (\r
+        (DEBUG_NET,\r
+        "\nIpReceive()  Exit #3  %Xh (%r)",\r
+        EFI_BUFFER_TOO_SMALL,\r
+        EFI_BUFFER_TOO_SMALL)\r
+        );\r
+\r
+      return EFI_BUFFER_TOO_SMALL;\r
+    }\r
+\r
+    /* Copy data into receive buffer.\r
+     */\r
+    if (DataLength != 0) {\r
+      DEBUG ((DEBUG_NET, "  To = %Xh", MessagePtr + FragmentOffset));\r
+\r
+      CopyMem (MessagePtr + FragmentOffset, NextHdrPtr, DataLength);\r
+      ByteCount += DataLength;\r
+    }\r
+\r
+    /* If we have seen the first and last fragments and\r
+     * the receive byte count is at least as large as the\r
+     * expected byte count, return SUCCESS.\r
+     *\r
+     * We could be tricked by receiving a fragment twice\r
+     * but the upper level protocol should figure this\r
+     * out.\r
+     */\r
+    if (GotFirstFragment && GotLastFragment && ByteCount >= ExpectedPacketLength) {\r
+      *MessageLengthPtr = ExpectedPacketLength;\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+}\r
+\r
+/* eof - pxe_bc_ip.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c
new file mode 100644 (file)
index 0000000..43e1bbb
--- /dev/null
@@ -0,0 +1,2193 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+    pxe_bc_mtftp.c\r
+\r
+Abstract:\r
+  TFTP and MTFTP (multicast TFTP) implementation.\r
+\r
+Revision History\r
+\r
+\r
+**/\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// The following #define is used to create a version that does not wait to\r
+// open after a listen.  This is just for a special regression test of MTFTP\r
+// server to make sure multiple opens are handled correctly.  Normally this\r
+// next line should be a comment.\r
+// #define SpecialNowaitVersion    // comment out for normal operation\r
+//\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+#include "bc.h"\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+UINT64\r
+Swap64 (\r
+  UINT64 n\r
+  )\r
+{\r
+  union {\r
+    UINT64  n;\r
+    UINT8   b[8];\r
+  } u;\r
+\r
+  UINT8 t;\r
+\r
+  u.n     = n;\r
+\r
+  t       = u.b[0];\r
+  u.b[0]  = u.b[7];\r
+  u.b[7]  = t;\r
+\r
+  t       = u.b[1];\r
+  u.b[1]  = u.b[6];\r
+  u.b[6]  = t;\r
+\r
+  t       = u.b[2];\r
+  u.b[2]  = u.b[5];\r
+  u.b[5]  = t;\r
+\r
+  t       = u.b[3];\r
+  u.b[3]  = u.b[4];\r
+  u.b[4]  = t;\r
+\r
+  return u.n;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS :=\r
+  @return EFI_TFTP_ERROR :=\r
+  @return other :=\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+TftpUdpRead (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  UINT16                      Operation,\r
+  VOID                        *HeaderPtr,\r
+  UINTN                       *BufferSizePtr,\r
+  VOID                        *BufferPtr,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
+  EFI_IP_ADDRESS              *OurIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
+  UINT16                      Timeout\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  EFI_STATUS              Status;\r
+  EFI_EVENT               TimeoutEvent;\r
+  UINTN                   HeaderSize;\r
+\r
+  //\r
+  //\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &TimeoutEvent\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  TimeoutEvent,\r
+                  TimerRelative,\r
+                  Timeout * 10000000 + 1000000\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return Status;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  HeaderSize = Private->BigBlkNumFlag ? sizeof (struct Tftpv4Ack8) : sizeof (struct Tftpv4Ack);\r
+\r
+#define ERROR_MESSAGE_PTR ((struct Tftpv4Error *) HeaderPtr)\r
+\r
+  Status = UdpRead (\r
+            Private,\r
+            Operation,\r
+            OurIpPtr,\r
+            OurPortPtr,\r
+            ServerIpPtr,\r
+            ServerPortPtr,\r
+            &HeaderSize,\r
+            HeaderPtr,\r
+            BufferSizePtr,\r
+            BufferPtr,\r
+            TimeoutEvent\r
+            );\r
+\r
+  if (Status != EFI_SUCCESS || ERROR_MESSAGE_PTR->OpCode != HTONS (TFTP_ERROR)) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return Status;\r
+  }\r
+  //\r
+  // got an error packet\r
+  // write one byte error code followed by error message\r
+  //\r
+  PxeBcMode                       = Private->EfiBc.Mode;\r
+  PxeBcMode->TftpErrorReceived    = TRUE;\r
+  PxeBcMode->TftpError.ErrorCode  = (UINT8) NTOHS (ERROR_MESSAGE_PTR->ErrCode);\r
+  HeaderSize                      = MIN (*BufferSizePtr, sizeof PxeBcMode->TftpError.ErrorString);\r
+  CopyMem (PxeBcMode->TftpError.ErrorString, BufferPtr, HeaderSize);\r
+\r
+  gBS->CloseEvent (TimeoutEvent);\r
+  return EFI_TFTP_ERROR;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+VOID\r
+SendError (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr\r
+  )\r
+{\r
+  struct Tftpv4Error  *ErrStr;\r
+  UINTN               Len;\r
+\r
+  ErrStr            = (VOID *) Private->TftpErrorBuffer;\r
+  Len               = sizeof *ErrStr;\r
+\r
+  ErrStr->OpCode    = HTONS (TFTP_ERROR);\r
+  ErrStr->ErrCode   = HTONS (TFTP_ERR_OPTION);\r
+  ErrStr->ErrMsg[0] = 0;\r
+\r
+  UdpWrite (\r
+    Private,\r
+    EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
+    ServerIpPtr,\r
+    ServerPortPtr,\r
+    0,\r
+    0,\r
+    OurPortPtr,\r
+    0,\r
+    0,\r
+    &Len,\r
+    ErrStr\r
+    );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SendAckAndGetData (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
+  EFI_IP_ADDRESS              *ReplyIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
+  UINT16                      Timeout,\r
+  UINTN                       *ReplyLenPtr,\r
+  UINT8                       *PxeBcMode,\r
+  UINT64                      *BlockNumPtr,\r
+  BOOLEAN                     AckOnly\r
+  )\r
+{\r
+  struct Tftpv4Data DataBuffer;\r
+  struct Tftpv4Ack  *Ack2Ptr;\r
+  struct Tftpv4Ack8 *Ack8Ptr;\r
+  EFI_STATUS        Status;\r
+  UINTN             Len;\r
+\r
+  Ack2Ptr = (VOID *) Private->TftpAckBuffer;\r
+  Ack8Ptr = (VOID *) Private->TftpAckBuffer;\r
+\r
+  if (Private->BigBlkNumFlag) {\r
+    Len               = sizeof (struct Tftpv4Ack8);\r
+\r
+    Ack8Ptr->OpCode   = HTONS (TFTP_ACK8);\r
+    Ack8Ptr->BlockNum = Swap64 (*BlockNumPtr);\r
+\r
+    Status = UdpWrite (\r
+              Private,\r
+              EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
+              ServerIpPtr,\r
+              ServerPortPtr,\r
+              0,\r
+              0,\r
+              OurPortPtr,\r
+              0,\r
+              0,\r
+              &Len,\r
+              Ack8Ptr\r
+              );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    Len               = sizeof (struct Tftpv4Ack);\r
+\r
+    Ack2Ptr->OpCode   = HTONS (TFTP_ACK);\r
+    Ack2Ptr->BlockNum = HTONS ((UINT16) *BlockNumPtr);\r
+\r
+    Status = UdpWrite (\r
+              Private,\r
+              EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
+              ServerIpPtr,\r
+              ServerPortPtr,\r
+              0,\r
+              0,\r
+              OurPortPtr,\r
+              0,\r
+              0,\r
+              &Len,\r
+              Ack2Ptr\r
+              );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  if (AckOnly) {\r
+    //\r
+    // ACK of last packet.  This is just a courtesy.\r
+    // Do not wait for response.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // read reply\r
+  //\r
+  Status = TftpUdpRead (\r
+            Private,\r
+            0,\r
+            &DataBuffer,\r
+            ReplyLenPtr,\r
+            PxeBcMode,\r
+            ServerIpPtr,\r
+            ServerPortPtr,\r
+            ReplyIpPtr,\r
+            OurPortPtr,\r
+            Timeout\r
+            );\r
+\r
+  if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {\r
+    return Status;\r
+  }\r
+  //\r
+  // got a good reply (so far)\r
+  // check for next data packet\r
+  //\r
+  if (!Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA)) {\r
+    if (Status == EFI_BUFFER_TOO_SMALL) {\r
+      SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);\r
+    }\r
+\r
+    *BlockNumPtr = NTOHS (DataBuffer.Header.BlockNum);\r
+    return Status;\r
+  }\r
+\r
+  if (Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA8)) {\r
+    if (Status == EFI_BUFFER_TOO_SMALL) {\r
+      SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);\r
+    }\r
+\r
+    *BlockNumPtr = Swap64 (*(UINT64 *) &DataBuffer.Header.BlockNum);\r
+    return Status;\r
+  }\r
+\r
+  return EFI_PROTOCOL_ERROR;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+LockStepReceive (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  UINTN                       PacketSize,\r
+  UINT64                      *BufferSizePtr,\r
+  UINT64                      Offset,\r
+  UINT8                       *BufferPtr,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
+  EFI_IP_ADDRESS              *ReplyIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
+  UINT64                      LastBlock,\r
+  UINT16                      Timeout,\r
+  IN BOOLEAN                  DontUseBuffer\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT64      BlockNum;\r
+  UINT64      BufferSize;\r
+  UINTN       Retries;\r
+  UINTN       SaveLen;\r
+  UINTN       ReplyLen;\r
+\r
+  ReplyLen  = PacketSize;\r
+  BlockNum  = LastBlock;\r
+\r
+  DEBUG ((DEBUG_INFO, "\nLockStepReceive()  PacketSize = %d", PacketSize));\r
+\r
+  if (DontUseBuffer) {\r
+    BufferSize = PacketSize;\r
+  } else {\r
+    BufferSize = *BufferSizePtr - Offset;\r
+    BufferPtr += Offset;\r
+  }\r
+\r
+  while (ReplyLen >= 512 && ReplyLen == PacketSize) {\r
+    if (BufferSize < PacketSize) {\r
+      ReplyLen = (UINTN) ((BufferSize > 0) ? BufferSize : 0);\r
+    }\r
+\r
+    SaveLen = ReplyLen;\r
+\r
+    //\r
+    // write an ack packet and get data - retry up to NUM_ACK_RETRIES on timeout\r
+    //\r
+    Retries = NUM_ACK_RETRIES;\r
+\r
+    do {\r
+      ReplyLen = SaveLen;\r
+\r
+      Status = SendAckAndGetData (\r
+                Private,\r
+                ServerIpPtr,\r
+                ServerPortPtr,\r
+                ReplyIpPtr,\r
+                OurPortPtr,\r
+                Timeout,\r
+                (UINTN *) &ReplyLen,\r
+                BufferPtr,\r
+                &BlockNum,\r
+                FALSE\r
+                );\r
+\r
+      if (!EFI_ERROR (Status) || Status == EFI_BUFFER_TOO_SMALL) {\r
+        if (BlockNum == LastBlock) {\r
+          DEBUG ((DEBUG_NET, "\nresend"));\r
+          //\r
+          // a resend - continue\r
+          //\r
+          Status = EFI_TIMEOUT;\r
+        } else if (Private->BigBlkNumFlag) {\r
+          if (BlockNum != ++LastBlock) {\r
+            DEBUG ((DEBUG_NET, "\nLockStepReceive()  Exit #1a"));\r
+            //\r
+            // not correct blocknum - error\r
+            //\r
+            return EFI_PROTOCOL_ERROR;\r
+          }\r
+        } else {\r
+          LastBlock = (LastBlock + 1) & 0xFFFF;\r
+          if (BlockNum != LastBlock) {\r
+            DEBUG ((DEBUG_NET, "\nLockStepReceive()  Exit #1b"));\r
+            return EFI_PROTOCOL_ERROR;\r
+            //\r
+            // not correct blocknum - error\r
+            //\r
+          }\r
+        }\r
+      }\r
+    } while (Status == EFI_TIMEOUT && --Retries);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      if (Status != EFI_BUFFER_TOO_SMALL) {\r
+        SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);\r
+      }\r
+\r
+      return Status;\r
+    }\r
+\r
+    if (DontUseBuffer) {\r
+      BufferSize += ReplyLen;\r
+    } else {\r
+      BufferPtr += ReplyLen;\r
+      BufferSize -= ReplyLen;\r
+    }\r
+  }\r
+  //\r
+  // while (ReplyLen == PacketSize);\r
+  //\r
+  if (DontUseBuffer) {\r
+    if (BufferSizePtr != NULL) {\r
+      *BufferSizePtr = (BufferSize - PacketSize);\r
+    }\r
+  } else {\r
+    *BufferSizePtr -= BufferSize;\r
+  }\r
+\r
+  /* Send ACK of last packet. */\r
+  ReplyLen = 0;\r
+\r
+  SendAckAndGetData (\r
+    Private,\r
+    ServerIpPtr,\r
+    ServerPortPtr,\r
+    ReplyIpPtr,\r
+    OurPortPtr,\r
+    Timeout,\r
+    (UINTN *) &ReplyLen,\r
+    BufferPtr,\r
+    &BlockNum,\r
+    TRUE\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// some literals\r
+//\r
+STATIC UINT8                      Mode[]          = MODE_BINARY;\r
+STATIC UINT8                      BlockSizeOp[]   = OP_BLKSIZE;\r
+STATIC UINT8                      TsizeOp[]       = OP_TFRSIZE;\r
+STATIC UINT8                      OverwriteOp[]   = OP_OVERWRITE;\r
+STATIC UINT8                      BigBlkNumOp[]   = OP_BIGBLKNUM;\r
+STATIC EFI_PXE_BASE_CODE_UDP_PORT TftpRequestPort = TFTP_OPEN_PORT;\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return Pointer to value field if option found or NULL if not found.\r
+\r
+**/\r
+STATIC\r
+UINT8 *\r
+FindOption (\r
+  UINT8 *OptionPtr,\r
+  INTN  OpLen,\r
+  UINT8 *OackPtr,\r
+  INTN  OackSize\r
+  )\r
+{\r
+  if ((OackSize -= OpLen) <= 0) {\r
+    return NULL;\r
+  }\r
+\r
+  do {\r
+    if (!CompareMem (OackPtr, OptionPtr, OpLen)) {\r
+      return OackPtr + OpLen;\r
+    }\r
+\r
+    ++OackPtr;\r
+  } while (--OackSize);\r
+\r
+  return NULL;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+#define BKSZOP      1 // block size\r
+#define TSIZEOP     2 // transfer size\r
+#define OVERWRITEOP 4 // overwrite\r
+#define BIGBLKNUMOP 8 // big block numbers\r
+STATIC\r
+EFI_STATUS\r
+TftpRwReq (\r
+  UINT16                      Req,\r
+  UINT16                      Options,\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
+  UINT8                       *FilenamePtr,\r
+  UINTN                       *PacketSizePtr,\r
+  VOID                        *Buffer\r
+  )\r
+{\r
+  union {\r
+    UINT8             Data[514];\r
+    struct Tftpv4Req  ReqStr;\r
+  } *u;\r
+\r
+  UINT16  OpFlags;\r
+  INTN    Len;\r
+  INTN    TotalLen;\r
+  UINT8   *Ptr;\r
+\r
+  if (*OurPortPtr == 0) {\r
+    OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT;\r
+  } else {\r
+    OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT;\r
+  }\r
+  //\r
+  // build the basic request - opcode, filename, mode\r
+  //\r
+  u                 = Buffer;\r
+  u->ReqStr.OpCode  = HTONS (Req);\r
+  TotalLen = sizeof (Mode) + sizeof (u->ReqStr.OpCode) + (Len = 1 + AsciiStrLen (FilenamePtr));\r
+\r
+  CopyMem (u->ReqStr.FileName, FilenamePtr, Len);\r
+  Ptr = (UINT8 *) (u->ReqStr.FileName + Len);\r
+\r
+  CopyMem (Ptr, Mode, sizeof (Mode));\r
+  Ptr += sizeof (Mode);\r
+\r
+  if (Options & BKSZOP) {\r
+    CopyMem (Ptr, BlockSizeOp, sizeof (BlockSizeOp));\r
+    UtoA10 (*PacketSizePtr, Ptr + sizeof (BlockSizeOp));\r
+\r
+    TotalLen += (Len = 1 + AsciiStrLen (Ptr + sizeof (BlockSizeOp)) + sizeof (BlockSizeOp));\r
+\r
+    Ptr += Len;\r
+  }\r
+\r
+  if (Options & TSIZEOP) {\r
+    CopyMem (Ptr, TsizeOp, sizeof (TsizeOp));\r
+    CopyMem (Ptr + sizeof (TsizeOp), "0", 2);\r
+    TotalLen += sizeof (TsizeOp) + 2;\r
+    Ptr += sizeof (TsizeOp) + 2;\r
+  }\r
+\r
+  if (Options & OVERWRITEOP) {\r
+    CopyMem (Ptr, OverwriteOp, sizeof (OverwriteOp));\r
+    CopyMem (Ptr + sizeof (OverwriteOp), "1", 2);\r
+    TotalLen += sizeof (OverwriteOp) + 2;\r
+    Ptr += sizeof (OverwriteOp) + 2;\r
+  }\r
+\r
+  if (Options & BIGBLKNUMOP) {\r
+    CopyMem (Ptr, BigBlkNumOp, sizeof (BigBlkNumOp));\r
+    CopyMem (Ptr + sizeof (BigBlkNumOp), "8", 2);\r
+    TotalLen += sizeof (BigBlkNumOp) + 2;\r
+    Ptr += sizeof (BigBlkNumOp) + 2;\r
+  }\r
+  //\r
+  // send it\r
+  //\r
+  return UdpWrite (\r
+          Private,\r
+          OpFlags,\r
+          ServerIpPtr,\r
+          ServerPortPtr,\r
+          0,\r
+          0,\r
+          OurPortPtr,\r
+          0,\r
+          0,\r
+          (UINTN *) &TotalLen,\r
+          u\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+TftpRwReqwResp (\r
+  UINT16                      Req,\r
+  UINT16                      Options,\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  VOID                        *HeaderPtr,\r
+  UINTN                       *PacketSizePtr,\r
+  UINTN                       *ReplyLenPtr,\r
+  VOID                        *BufferPtr,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *ServerReplyPortPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
+  UINT8                       *FilenamePtr,\r
+  UINT16                      Timeout\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINTN       SaveReplyLen;\r
+  INTN        Retries;\r
+  UINT8       Buffer[514];\r
+\r
+  SaveReplyLen            = *ReplyLenPtr;\r
+  Retries                 = 3;\r
+  Private->BigBlkNumFlag  = FALSE;\r
+  *OurPortPtr             = 0;\r
+  //\r
+  // generate random\r
+  //\r
+  do {\r
+    if (*OurPortPtr != 0) {\r
+      if (++ *OurPortPtr == 0) {\r
+        *OurPortPtr = PXE_RND_PORT_LOW;\r
+      }\r
+    }\r
+    //\r
+    // send request from our Ip = StationIp\r
+    //\r
+    if ((Status = TftpRwReq (\r
+                    Req,\r
+                    Options,\r
+                    Private,\r
+                    ServerIpPtr,\r
+                    ServerPortPtr,\r
+                    OurPortPtr,\r
+                    FilenamePtr,\r
+                    PacketSizePtr,\r
+                    Buffer\r
+                    )) != EFI_SUCCESS) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nTftpRwReqwResp()  Exit #1  %xh (%r)",\r
+        Status,\r
+        Status)\r
+        );\r
+\r
+      return Status;\r
+    }\r
+    //\r
+    // read reply to our Ip = StationIp\r
+    //\r
+    *ReplyLenPtr = SaveReplyLen;\r
+\r
+    Status = TftpUdpRead (\r
+              Private,\r
+              EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,\r
+              HeaderPtr,\r
+              ReplyLenPtr,\r
+              BufferPtr,\r
+              ServerIpPtr,\r
+              ServerReplyPortPtr,\r
+              0,\r
+              OurPortPtr,\r
+              Timeout\r
+              );\r
+  } while (Status == EFI_TIMEOUT && --Retries);\r
+\r
+  if (!Options || Status != EFI_TFTP_ERROR) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nTftpRwReqwResp()  Exit #2  %xh (%r)",\r
+      Status,\r
+      Status)\r
+      );\r
+    return Status;\r
+  }\r
+\r
+  Status = TftpRwReqwResp (\r
+            Req,\r
+            0,\r
+            Private,\r
+            HeaderPtr,\r
+            PacketSizePtr,\r
+            ReplyLenPtr,\r
+            BufferPtr,\r
+            ServerIpPtr,\r
+            ServerPortPtr,\r
+            ServerReplyPortPtr,\r
+            OurPortPtr,\r
+            FilenamePtr,\r
+            Timeout\r
+            );\r
+\r
+  DEBUG ((DEBUG_WARN, "\nTftpRwReqwResp()  Exit #3  %xh (%r)", Status, Status));\r
+\r
+  return Status;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// mtftp listen\r
+// read on mcast ip, cport, from sport, for data packet\r
+// returns success if gets multicast last packet or all up to last block\r
+// if not missing, then finished\r
+//\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+MtftpListen (\r
+  PXE_BASECODE_DEVICE           *Private,\r
+  UINT64                        *BufferSizePtr,\r
+  UINT8                         *BufferPtr,\r
+  EFI_IP_ADDRESS                *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_MTFTP_INFO  *MtftpInfoPtr,\r
+  UINT64                        *StartBlockPtr,\r
+  UINTN                         *NumMissedPtr,\r
+  UINT16                        TransTimeout,\r
+  UINT16                        ListenTimeout,\r
+  UINT64                        FinalBlock,\r
+  IN BOOLEAN                    DontUseBuffer\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  struct Tftpv4Ack  Header;\r
+  UINT64            Offset;\r
+  UINT64            BlockNum;\r
+  UINT64            LastBlockNum;\r
+  UINT64            BufferSize;\r
+  UINTN             NumMissed;\r
+  UINTN             PacketSize;\r
+  UINTN             SaveReplyLen;\r
+  UINTN             ReplyLen;\r
+  UINT16            Timeout;\r
+\r
+  LastBlockNum  = *StartBlockPtr;\r
+  Timeout       = ListenTimeout;\r
+  *NumMissedPtr = 0;\r
+  PacketSize    = 0;\r
+  BufferSize    = *BufferSizePtr;\r
+  ReplyLen      = MAX_TFTP_PKT_SIZE;;\r
+\r
+  //\r
+  // receive\r
+  //\r
+  do {\r
+    if ((SaveReplyLen = ReplyLen) > BufferSize) {\r
+      SaveReplyLen = (UINTN) BufferSize;\r
+    }\r
+\r
+    /* %%TBD - add big block number support */\r
+\r
+    //\r
+    // get data - loop on resends\r
+    //\r
+    do {\r
+      ReplyLen = SaveReplyLen;\r
+\r
+      if ((Status = TftpUdpRead (\r
+                      Private,\r
+                      0,\r
+                      &Header,\r
+                      &ReplyLen,\r
+                      BufferPtr,\r
+                      ServerIpPtr,\r
+                      &MtftpInfoPtr->SPort,\r
+                      &MtftpInfoPtr->MCastIp,\r
+                      &MtftpInfoPtr->CPort,\r
+                      Timeout\r
+                      )) != EFI_SUCCESS) {\r
+        return Status;\r
+      }\r
+      //\r
+      // make sure a data packet\r
+      //\r
+      if (Header.OpCode != HTONS (TFTP_DATA)) {\r
+        return EFI_PROTOCOL_ERROR;\r
+      }\r
+    } while ((BlockNum = NTOHS (Header.BlockNum)) == LastBlockNum);\r
+\r
+    //\r
+    // make sure still going up\r
+    //\r
+    if (LastBlockNum > BlockNum) {\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+\r
+    if (BlockNum - LastBlockNum > 0xFFFFFFFF) {\r
+      return EFI_PROTOCOL_ERROR;\r
+    } else {\r
+      NumMissed = (UINTN) (BlockNum - LastBlockNum - 1);\r
+    }\r
+\r
+    LastBlockNum = BlockNum;\r
+\r
+    //\r
+    // if first time through, some reinitialization\r
+    //\r
+    if (!PacketSize) {\r
+      *StartBlockPtr  = BlockNum;\r
+      PacketSize      = ReplyLen;\r
+      Timeout         = TransTimeout;\r
+    } else {\r
+      *NumMissedPtr = (UINT16) (*NumMissedPtr + NumMissed);\r
+    }\r
+    //\r
+    // if missed packets, update start block,\r
+    // etc. and move packet to proper place in buffer\r
+    //\r
+    if (NumMissed) {\r
+      *StartBlockPtr = BlockNum;\r
+      if (!DontUseBuffer) {\r
+        Offset = NumMissed * PacketSize;\r
+        CopyMem (BufferPtr + Offset, BufferPtr, ReplyLen);\r
+        BufferPtr += Offset;\r
+        BufferSize -= Offset;\r
+      }\r
+    }\r
+\r
+    if (!DontUseBuffer) {\r
+      BufferPtr += ReplyLen;\r
+      BufferSize -= ReplyLen;\r
+    }\r
+  } while (ReplyLen == PacketSize && BlockNum != FinalBlock);\r
+\r
+  *BufferSizePtr = BufferSize;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return // mtftp open session\r
+  @return // return code EFI_SUCCESS\r
+  @return //      and *CompletionStatusPtr = GOTUNI | GOTMULTI means done\r
+  @return //      and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest\r
+  @return //      and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all\r
+  @retval GOTUNI  returns NO_DATA go will go to TFTP session)\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+MtftpOpen (\r
+  PXE_BASECODE_DEVICE                                               * Private,\r
+  UINT64                                                            *BufferSizePtr,\r
+  UINT8                                                             *BufferPtr,\r
+  UINTN                                                             *PacketSizePtr,\r
+  EFI_IP_ADDRESS                                                    * ServerIpPtr,\r
+  UINT8                                                             *FilenamePtr,\r
+  EFI_PXE_BASE_CODE_MTFTP_INFO                                      * MtftpInfoPtr,\r
+  UINT8                                                             *CompletionStatusPtr,\r
+#define GOTUNI 1\r
+#define GOTMULTI 2\r
+  IN BOOLEAN                    DontUseBuffer\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  EFI_IP_ADDRESS    OurReplyIp;\r
+  struct Tftpv4Ack  Header;\r
+  INTN              ReplyLen;\r
+  INTN              Retries;\r
+  UINT8             *BufferPtr2;\r
+  UINT8             TmpBuf[514];\r
+\r
+  Retries         = NUM_MTFTP_OPEN_RETRIES;\r
+  BufferPtr2      = BufferPtr;\r
+  *PacketSizePtr  = (UINTN) (MIN (*BufferSizePtr, MAX_TFTP_PKT_SIZE));\r
+\r
+  do {\r
+    //\r
+    // send a read request\r
+    //\r
+    *CompletionStatusPtr = 0;\r
+\r
+    if ((Status = TftpRwReq (\r
+                    TFTP_RRQ,\r
+                    0,\r
+                    Private,\r
+                    ServerIpPtr,\r
+                    &MtftpInfoPtr->SPort,\r
+                    &MtftpInfoPtr->CPort,\r
+                    FilenamePtr,\r
+                    PacketSizePtr,\r
+                    TmpBuf\r
+                    )) != EFI_SUCCESS) {\r
+      return Status;\r
+    }\r
+\r
+    for (;;) {\r
+      //\r
+      // read reply\r
+      //\r
+      ZeroMem (&OurReplyIp, Private->IpLength);\r
+      ReplyLen = *PacketSizePtr;\r
+\r
+      if ((Status = TftpUdpRead (\r
+                      Private,\r
+                      EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER,\r
+                      &Header,\r
+                      (UINTN *) &ReplyLen,\r
+                      BufferPtr2,\r
+                      ServerIpPtr,\r
+                      &MtftpInfoPtr->SPort,\r
+                      &OurReplyIp,\r
+                      &MtftpInfoPtr->CPort,\r
+                      MtftpInfoPtr->TransmitTimeout\r
+                      )) == EFI_SUCCESS) {\r
+        //\r
+        // check for first data packet\r
+        //\r
+        if (Header.OpCode != HTONS (TFTP_DATA)) {\r
+          return EFI_PROTOCOL_ERROR;\r
+        }\r
+        //\r
+        // check block num\r
+        //\r
+        if (Header.BlockNum != HTONS (1)) {\r
+          //\r
+          // it's not first\r
+          // if we are not the primary client,\r
+          // we probably got first and now second\r
+          // multicast but no unicast, so\r
+          // *CompletionStatusPtr = GOTMULTI - if this is\r
+          // the second, can just go on to listen\r
+          // starting with 2 as the last block\r
+          // received\r
+          //\r
+          if (Header.BlockNum != HTONS (2)) {\r
+            //\r
+            // not second\r
+            //\r
+            *CompletionStatusPtr = 0;\r
+          }\r
+\r
+          return Status;\r
+        }\r
+\r
+        //\r
+        // now actual\r
+        //\r
+        *PacketSizePtr = ReplyLen;\r
+        //\r
+        // see if a unicast data packet\r
+        //\r
+        if (!CompareMem (\r
+              &OurReplyIp,\r
+              &Private->EfiBc.Mode->StationIp,\r
+              Private->IpLength\r
+              )) {\r
+          *CompletionStatusPtr |= GOTUNI;\r
+          //\r
+          // it is\r
+          // if already got multicast packet,\r
+          // got em both\r
+          //\r
+          if (*CompletionStatusPtr & GOTMULTI) {\r
+            break;\r
+          }\r
+        } else if (!CompareMem (\r
+                    &OurReplyIp,\r
+                    &MtftpInfoPtr->MCastIp,\r
+                    Private->IpLength\r
+                    )) {\r
+          //\r
+          // otherwise see if a multicast data packet\r
+          //\r
+          *CompletionStatusPtr |= GOTMULTI;\r
+          //\r
+          // it is\r
+          // got first - bump pointer so that if\r
+          // second multi comes along, we're OK\r
+          //\r
+          if (!DontUseBuffer) {\r
+            BufferPtr2 = (UINT8 *) BufferPtr + ReplyLen;\r
+          }\r
+          //\r
+          // if already got unicast packet,\r
+          // got em both\r
+          //\r
+          if (*CompletionStatusPtr & GOTUNI) {\r
+            break;\r
+          }\r
+        } else {\r
+          //\r
+          // else protocol error\r
+          //\r
+          return EFI_PROTOCOL_ERROR;\r
+        }\r
+      } else if (Status == EFI_TIMEOUT) {\r
+        //\r
+        // bad return code - if timed out, retry\r
+        //\r
+        break;\r
+      } else {\r
+        //\r
+        // else just bad - failed MTFTP open\r
+        //\r
+        return Status;\r
+      }\r
+    }\r
+  } while (Status == EFI_TIMEOUT && --Retries);\r
+\r
+  if (Status != EFI_SUCCESS) {\r
+    //\r
+    // open failed\r
+    //\r
+    return Status;\r
+  }\r
+  //\r
+  // got em both - go into receive mode\r
+  // routine to read rest of file after a successful open (TFTP or MTFTP)\r
+  // sends ACK and gets next data packet until short packet arrives,\r
+  // then sends ACK and (hopefully) times out\r
+  //\r
+  return LockStepReceive (\r
+          Private,\r
+          (UINT16) ReplyLen,\r
+          BufferSizePtr,\r
+          ReplyLen,\r
+          BufferPtr,\r
+          ServerIpPtr,\r
+          &MtftpInfoPtr->SPort,\r
+          &MtftpInfoPtr->MCastIp,\r
+          &MtftpInfoPtr->CPort,\r
+          1,\r
+          MtftpInfoPtr->TransmitTimeout,\r
+          DontUseBuffer\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+MtftpDownload (\r
+  PXE_BASECODE_DEVICE           *Private,\r
+  UINT64                        *BufferSizePtr,\r
+  UINT8                         *BufferPtr,\r
+  EFI_IP_ADDRESS                *ServerIpPtr,\r
+  UINT8                         *FilenamePtr,\r
+  EFI_PXE_BASE_CODE_MTFTP_INFO  *MtftpInfoPtr,\r
+  IN BOOLEAN                    DontUseBuffer\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_IP_FILTER Filter;\r
+  EFI_STATUS                  Status;\r
+  UINT64                      StartBlock;\r
+  UINT64                      LastBlock;\r
+  UINT64                      LastStartBlock;\r
+  UINT64                      BufferSize;\r
+  UINTN                       Offset;\r
+  UINTN                       NumMissed;\r
+  UINT16                      TransTimeout;\r
+  UINT16                      ListenTimeout;\r
+  UINT8                       *BufferPtrLocal;\r
+\r
+  TransTimeout      = MtftpInfoPtr->TransmitTimeout;\r
+  ListenTimeout     = MtftpInfoPtr->ListenTimeout;\r
+  LastBlock         = 0;\r
+  LastStartBlock    = 0;\r
+  Offset            = 0;\r
+\r
+  Filter.Filters    = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;\r
+  Filter.IpCnt      = 2;\r
+  Filter.IpList[0]  = Private->EfiBc.Mode->StationIp;\r
+  Filter.IpList[1]  = MtftpInfoPtr->MCastIp;\r
+\r
+  if ((Status = IpFilter (Private, &Filter)) != EFI_SUCCESS) {\r
+    return Status;\r
+  }\r
+\r
+  for (;;) {\r
+    StartBlock  = LastStartBlock;\r
+    BufferSize  = *BufferSizePtr - Offset;\r
+\r
+    if (DontUseBuffer) {\r
+    //\r
+    // overwrie the temp buf\r
+    //\r
+      BufferPtrLocal = BufferPtr;\r
+    } else {\r
+      BufferPtrLocal = BufferPtr + Offset;\r
+\r
+    }\r
+    //\r
+    // special !!! do not leave enabled in saved version on Source Safe\r
+    // Following code put in in order to create a special version for regression\r
+    // test of MTFTP server to make sure it handles mulitple opens correctly.\r
+    // This code should NOT be enabled normally.\r
+    //\r
+#ifdef SpecialNowaitVersion\r
+#pragma message ("This is special version for MTFTP regression test")\r
+    if (StartBlock || !LastBlock)\r
+#endif\r
+      if (((Status = MtftpListen (\r
+                      Private,\r
+                      &BufferSize,\r
+                      BufferPtrLocal,\r
+                      ServerIpPtr,\r
+                      MtftpInfoPtr,\r
+                      &StartBlock,\r
+                      &NumMissed,\r
+                      TransTimeout,\r
+                      ListenTimeout,\r
+                      LastBlock,\r
+                      DontUseBuffer\r
+                      )) != EFI_SUCCESS) && (Status != EFI_TIMEOUT)) {\r
+        return Status;\r
+        //\r
+        // failed\r
+        //\r
+      }\r
+    //\r
+    // if none were received, start block is not reset\r
+    //\r
+    if (StartBlock == LastStartBlock) {\r
+      UINT8 CompStat;\r
+\r
+      //\r
+      // timed out with none received - try MTFTP open\r
+      //\r
+      if ((Status = MtftpOpen (\r
+                      Private,\r
+                      BufferSizePtr,\r
+                      BufferPtr,\r
+                      &Offset,\r
+                      ServerIpPtr,\r
+                      FilenamePtr,\r
+                      MtftpInfoPtr,\r
+                      &CompStat,\r
+                      DontUseBuffer\r
+                      )) != EFI_SUCCESS) {\r
+        //\r
+        // open failure - try TFTP\r
+        //\r
+        return Status;\r
+      }\r
+      //\r
+      // return code EFI_SUCCESS\r
+      // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done\r
+      // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest\r
+      // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all\r
+      // (do not get = GOTUNI - returns NO_DATA go will go to TFTP session)\r
+      //\r
+      if (CompStat == (GOTUNI | GOTMULTI)) {\r
+      //\r
+      // finished - got it all\r
+      //\r
+        return Status;\r
+      }\r
+\r
+      if (CompStat) {\r
+        //\r
+        // offset is two packet lengths\r
+        //\r
+        Offset <<= 1;\r
+        //\r
+        // last block received\r
+        //\r
+        LastStartBlock = 2;\r
+      } else {\r
+        Offset          = 0;\r
+        LastStartBlock  = 0;\r
+      }\r
+\r
+      ListenTimeout = TransTimeout;\r
+      continue;\r
+    }\r
+    //\r
+    // did we get the last block\r
+    //\r
+    if (Status == EFI_SUCCESS) {\r
+      //\r
+      // yes - set the file size if this was first time\r
+      //\r
+      if (!LastBlock) {\r
+        *BufferSizePtr -= BufferSize;\r
+      }\r
+      //\r
+      // if buffer was too small, finished\r
+      //\r
+      if (!DontUseBuffer) {\r
+        return EFI_BUFFER_TOO_SMALL;\r
+      }\r
+      //\r
+      // if we got them all, finished\r
+      //\r
+      if (!NumMissed && StartBlock == LastStartBlock + 1) {\r
+        return Status;\r
+      }\r
+      //\r
+      // did not get them all - set last block\r
+      //\r
+      LastBlock = (UINT16) (StartBlock - 1);\r
+    }\r
+    //\r
+    // compute listen timeout\r
+    //\r
+    ListenTimeout = (UINT16) ((NumMissed > MtftpInfoPtr->ListenTimeout) ? 0 : (MtftpInfoPtr->ListenTimeout - NumMissed));\r
+\r
+    //\r
+    // reset\r
+    //\r
+    Offset          = 0;\r
+    LastStartBlock  = 0;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+TftpInfo (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  UINT64                      *BufferSizePtr,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  SrvPort,\r
+  UINT8                       *FilenamePtr,\r
+  UINTN                       *PacketSizePtr\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT  OurPort;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  ServerReplyPort;\r
+  EFI_STATUS                  Status;\r
+  UINT64                      BlockNum;\r
+  UINTN                       Offset;\r
+  UINTN                       ReplyLen;\r
+  UINT8                       *Ptr;\r
+\r
+  union {\r
+    struct Tftpv4Oack OAck2Ptr;\r
+    struct Tftpv4Ack  Ack2Ptr;\r
+    struct Tftpv4Data Datastr;\r
+  } u;\r
+\r
+  OurPort         = 0;\r
+  ServerReplyPort = 0;\r
+  ReplyLen        = sizeof (u.Datastr.Data);\r
+\r
+  //\r
+  // send a write request with the blocksize option -\r
+  // sets our IP and port - and receive reply - sets his port\r
+  // will retry operation up to 3 times if no response,\r
+  // and will retry without options on an error reply\r
+  //\r
+  if ((Status = TftpRwReqwResp (\r
+                  TFTP_RRQ,\r
+                  /* BIGBLKNUMOP | */BKSZOP | TSIZEOP,\r
+                  Private,\r
+                  &u,\r
+                  PacketSizePtr,\r
+                  &ReplyLen,\r
+                  u.Datastr.Data,\r
+                  ServerIpPtr,\r
+                  &SrvPort,\r
+                  &ServerReplyPort,\r
+                  &OurPort,\r
+                  FilenamePtr,\r
+                  REQ_RESP_TIMEOUT\r
+                  )) != EFI_SUCCESS) {\r
+    DEBUG ((DEBUG_WARN, "\nTftpInfo()  Exit #1"));\r
+    return Status;\r
+  }\r
+  //\r
+  // check for good OACK\r
+  //\r
+  if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {\r
+    //\r
+    // now parse it for options\r
+    // bigblk#\r
+    //\r
+    Ptr = FindOption (\r
+            BigBlkNumOp,\r
+            sizeof (BigBlkNumOp),\r
+            u.OAck2Ptr.OpAck[0].Option,\r
+            ReplyLen + sizeof (u.Ack2Ptr.BlockNum)\r
+            );\r
+\r
+    if (Ptr != NULL) {\r
+      if (AtoU (Ptr) == 8) {\r
+        Private->BigBlkNumFlag = TRUE;\r
+      } else {\r
+        return EFI_PROTOCOL_ERROR;\r
+      }\r
+    }\r
+    //\r
+    // blksize\r
+    //\r
+    Ptr = FindOption (\r
+            BlockSizeOp,\r
+            sizeof (BlockSizeOp),\r
+            u.OAck2Ptr.OpAck[0].Option,\r
+            ReplyLen += sizeof (u.Ack2Ptr.BlockNum)\r
+            );\r
+\r
+    *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512;\r
+\r
+    //\r
+    // tsize\r
+    //\r
+    Ptr = FindOption (\r
+            TsizeOp,\r
+            sizeof (TsizeOp),\r
+            u.OAck2Ptr.OpAck[0].Option,\r
+            ReplyLen\r
+            );\r
+\r
+    if (Ptr != NULL) {\r
+      *BufferSizePtr = AtoU64 (Ptr);\r
+\r
+      //\r
+      // teminate session with error\r
+      //\r
+      SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort);\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    Offset    = 0;\r
+    BlockNum  = 0;\r
+  } else {\r
+    //\r
+    // if MTFTP get filesize, return unsupported\r
+    //\r
+    if (SrvPort != TftpRequestPort) {\r
+      SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort);\r
+      DEBUG ((DEBUG_WARN, "\nTftpInfo()  Exit #3"));\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+\r
+    Offset    = ReplyLen;\r
+    //\r
+    // last block received\r
+    //\r
+    BlockNum  = 1;\r
+  }\r
+  //\r
+  // does not support the option - do a download with no buffer\r
+  //\r
+  *BufferSizePtr = 0;\r
+\r
+  Status = LockStepReceive (\r
+            Private,\r
+            (UINT16) ReplyLen,\r
+            BufferSizePtr,\r
+            Offset,\r
+            (INT8 *) &u,\r
+            ServerIpPtr,\r
+            &ServerReplyPort,\r
+            &Private->EfiBc.Mode->StationIp,\r
+            &OurPort,\r
+            BlockNum,\r
+            ACK_TIMEOUT,\r
+            TRUE\r
+            );\r
+\r
+  if (Status != EFI_SUCCESS) {\r
+    DEBUG ((DEBUG_WARN, "\nTftpInfo()  LockStepReceive() == %Xh", Status));\r
+  }\r
+\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+TftpDownload (\r
+  PXE_BASECODE_DEVICE         *Private,\r
+  UINT64                      *BufferSizePtr,\r
+  UINT8                       *BufferPtr,\r
+  EFI_IP_ADDRESS              *ServerIpPtr,\r
+  UINT8                       *FilenamePtr,\r
+  UINTN                       *PacketSizePtr,\r
+  EFI_PXE_BASE_CODE_UDP_PORT  SrvPort,\r
+  UINT16                      Req,\r
+  IN BOOLEAN                  DontUseBuffer\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT  OurPort;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  ServerReplyPort;\r
+  EFI_STATUS                  Status;\r
+  UINT64                      Offset;\r
+  UINT64                      BlockNum;\r
+  UINTN                       ReplyLen;\r
+  UINT8                       *Ptr;\r
+\r
+  union {\r
+    struct Tftpv4Ack    Ack2Ptr;\r
+    struct Tftpv4Oack   OAck2Ptr;\r
+    struct Tftpv4Data   Data;\r
+    struct Tftpv4Ack8   Ack8Ptr;\r
+    struct Tftpv4Data8  Data8;\r
+  } U;\r
+\r
+  OurPort         = 0;\r
+  ServerReplyPort = 0;\r
+  ReplyLen        = (UINTN) ((*BufferSizePtr > 0xFFFF) ? 0xFFFF : *BufferSizePtr);\r
+\r
+  //\r
+  // send a read request with the blocksize option - sets our IP and port\r
+  // - and receive reply - sets his port will retry operation up to 3\r
+  // times if no response, and will retry without options on an error\r
+  // reply\r
+  //\r
+  if ((Status = TftpRwReqwResp (\r
+                  Req,\r
+                  /* BIGBLKNUMOP | */BKSZOP,\r
+                  Private,\r
+                  &U,\r
+                  PacketSizePtr,\r
+                  &ReplyLen,\r
+                  BufferPtr,\r
+                  ServerIpPtr,\r
+                  &SrvPort,\r
+                  &ServerReplyPort,\r
+                  &OurPort,\r
+                  FilenamePtr,\r
+                  REQ_RESP_TIMEOUT\r
+                  )) != EFI_SUCCESS) {\r
+    DEBUG ((DEBUG_WARN, "\nTftpDownload()  Exit #1  %xh (%r)", Status, Status));\r
+    return Status;\r
+  }\r
+  //\r
+  // check for OACK\r
+  //\r
+  if (U.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {\r
+    //\r
+    // get the OACK\r
+    //\r
+    CopyMem (U.Data.Data, BufferPtr, ReplyLen);\r
+\r
+    Ptr = FindOption (\r
+            BigBlkNumOp,\r
+            sizeof (BigBlkNumOp),\r
+            U.OAck2Ptr.OpAck[0].Option,\r
+            ReplyLen + sizeof (U.Ack2Ptr.BlockNum)\r
+            );\r
+\r
+    if (Ptr != NULL) {\r
+      if (AtoU (Ptr) == 8) {\r
+        Private->BigBlkNumFlag = TRUE;\r
+      } else {\r
+        return EFI_PROTOCOL_ERROR;\r
+      }\r
+    }\r
+    //\r
+    // now parse it for blocksize option\r
+    //\r
+    Ptr = FindOption (\r
+            BlockSizeOp,\r
+            sizeof (BlockSizeOp),\r
+            U.OAck2Ptr.OpAck[0].Option,\r
+            ReplyLen += sizeof (U.Ack2Ptr.BlockNum)\r
+            );\r
+\r
+    ReplyLen  = (Ptr != NULL) ? AtoU (Ptr) : 512;\r
+\r
+    Offset    = 0;\r
+    //\r
+    // last block received\r
+    //\r
+    BlockNum  = 0;\r
+  } else if (U.Ack2Ptr.OpCode != HTONS (TFTP_DATA) || U.Ack2Ptr.BlockNum != HTONS (1)) {\r
+    //\r
+    // or data\r
+    //\r
+    DEBUG ((DEBUG_WARN, "\nTftpDownload()  Exit #2  %xh (%r)", Status, Status));\r
+\r
+    return EFI_PROTOCOL_ERROR;\r
+  } else {\r
+    //\r
+    // got good data packet\r
+    //\r
+    Offset    = ReplyLen;\r
+    //\r
+    // last block received\r
+    //\r
+    BlockNum  = 1;\r
+  }\r
+\r
+  if (PacketSizePtr != NULL) {\r
+    *PacketSizePtr = ReplyLen;\r
+  }\r
+  //\r
+  // routine to read rest of file after a successful open (TFTP or MTFTP)\r
+  // sends ACK and gets next data packet until short packet arrives, then sends\r
+  // ACK and (hopefully) times out\r
+  // if first packet has been read, BufferPtr and BufferSize must reflect fact\r
+  //\r
+  Status = LockStepReceive (\r
+            Private,\r
+            ReplyLen,\r
+            BufferSizePtr,\r
+            Offset,\r
+            BufferPtr,\r
+            ServerIpPtr,\r
+            &ServerReplyPort,\r
+            &Private->EfiBc.Mode->StationIp,\r
+            &OurPort,\r
+            BlockNum,\r
+            ACK_TIMEOUT,\r
+            DontUseBuffer\r
+            );\r
+\r
+  if (Status != EFI_SUCCESS) {\r
+    DEBUG ((DEBUG_WARN, "\nTftpDownload()  Exit #3  %xh (%r)", Status, Status));\r
+\r
+    if (Status == EFI_BUFFER_TOO_SMALL) {\r
+      Status = TftpInfo (\r
+                Private,\r
+                BufferSizePtr,\r
+                ServerIpPtr,\r
+                SrvPort,\r
+                FilenamePtr,\r
+                PacketSizePtr\r
+                );\r
+\r
+      if (!EFI_ERROR (Status)) {\r
+        Status = EFI_BUFFER_TOO_SMALL;\r
+      }\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+TftpUpload (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  UINT64              *BufferSizePtr,\r
+  VOID                *BufferPtr,\r
+  EFI_IP_ADDRESS      *ServerIpPtr,\r
+  UINT8               *FilenamePtr,\r
+  UINTN               *PacketSizePtr,\r
+  BOOLEAN             Overwrite\r
+  )\r
+{\r
+  struct Tftpv4Ack            Header;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  OurPort;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  ServerReplyPort;\r
+  EFI_STATUS                  Status;\r
+  UINT64                      BlockNum;\r
+  UINT64                      TransferSize;\r
+  UINTN                       ReplyLen;\r
+  UINTN                       TransferLen;\r
+  UINT16                      Options;\r
+  UINT8                       *Ptr;\r
+\r
+  union {\r
+    struct Tftpv4Oack OAck2Ptr;\r
+    struct Tftpv4Ack  Ack2Ptr;\r
+    struct Tftpv4Data Datastr;\r
+  } u;\r
+\r
+  OurPort         = 0;\r
+  ServerReplyPort = 0;\r
+  TransferSize    = *BufferSizePtr;\r
+  ReplyLen        = sizeof (u.Datastr.Data);\r
+  Options         = (UINT16) ((Overwrite) ? OVERWRITEOP | BKSZOP : BKSZOP);\r
+\r
+  //\r
+  // send a write request with the blocksize option - sets our IP and port -\r
+  // and receive reply - sets his port\r
+  // will retry operation up to 3 times if no response, and will retry without\r
+  // options on an error reply\r
+  //\r
+  if ((Status = TftpRwReqwResp (\r
+                  TFTP_WRQ,\r
+                  Options,\r
+                  Private,\r
+                  &u,\r
+                  PacketSizePtr,\r
+                  &ReplyLen,\r
+                  u.Datastr.Data,\r
+                  ServerIpPtr,\r
+                  &TftpRequestPort,\r
+                  &ServerReplyPort,\r
+                  &OurPort,\r
+                  FilenamePtr,\r
+                  REQ_RESP_TIMEOUT\r
+                  )) != EFI_SUCCESS) {\r
+    return Status;\r
+  }\r
+  //\r
+  // check for OACK\r
+  //\r
+  if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {\r
+    //\r
+    // parse it for blocksize option\r
+    //\r
+    Ptr = FindOption (\r
+            BlockSizeOp,\r
+            sizeof (BlockSizeOp),\r
+            u.OAck2Ptr.OpAck[0].Option,\r
+            ReplyLen += sizeof (u.Ack2Ptr.BlockNum)\r
+            );\r
+    *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512;\r
+  }\r
+  //\r
+  // or ACK\r
+  //\r
+  else if (u.Ack2Ptr.OpCode == HTONS (TFTP_ACK)) {\r
+    //\r
+    // option was not supported\r
+    //\r
+    *PacketSizePtr = 512;\r
+  } else {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+  //\r
+  // loop\r
+  //\r
+  Header.OpCode   = HTONS (TFTP_DATA);\r
+  BlockNum        = 1;\r
+  Header.BlockNum = HTONS (1);\r
+\r
+  do {\r
+    UINTN HeaderSize;\r
+    INTN  Retries;\r
+\r
+    Retries     = NUM_ACK_RETRIES;\r
+    HeaderSize  = sizeof (Header);\r
+    TransferLen = (UINTN) (MIN (*PacketSizePtr, TransferSize));\r
+\r
+    //\r
+    // write a data packet and get an ack\r
+    //\r
+    do {\r
+      //\r
+      // write\r
+      //\r
+      if ((Status = UdpWrite (\r
+                      Private,\r
+                      EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
+                      ServerIpPtr,\r
+                      &ServerReplyPort,\r
+                      0,\r
+                      0,\r
+                      &OurPort,\r
+                      &HeaderSize,\r
+                      &Header,\r
+                      &TransferLen,\r
+                      BufferPtr\r
+                      )) != EFI_SUCCESS) {\r
+        return Status;\r
+      }\r
+      //\r
+      // read reply\r
+      //\r
+      ReplyLen = sizeof (u.Datastr.Data);\r
+\r
+      if ((Status = TftpUdpRead (\r
+                      Private,\r
+                      0,\r
+                      &u,\r
+                      &ReplyLen,\r
+                      u.Datastr.Data,\r
+                      ServerIpPtr,\r
+                      &ServerReplyPort,\r
+                      0,\r
+                      &OurPort,\r
+                      ACK_TIMEOUT\r
+                      )) == EFI_SUCCESS) {\r
+        //\r
+        // check for ACK for this data packet\r
+        //\r
+        if (u.Ack2Ptr.OpCode != HTONS (TFTP_ACK)) {\r
+          return EFI_PROTOCOL_ERROR;\r
+        }\r
+\r
+        if (u.Ack2Ptr.BlockNum != Header.BlockNum) {\r
+          //\r
+          // not for this packet - continue\r
+          //\r
+          Status = EFI_TIMEOUT;\r
+        }\r
+      }\r
+    } while (Status == EFI_TIMEOUT && --Retries);\r
+\r
+    if (Status != EFI_SUCCESS) {\r
+      return Status;\r
+    }\r
+\r
+    BufferPtr = (VOID *) ((UINT8 *) (BufferPtr) + TransferLen);\r
+    TransferSize -= TransferLen;\r
+    ++BlockNum;\r
+    Header.BlockNum = HTONS ((UINT16) BlockNum);\r
+  } while (TransferLen == *PacketSizePtr);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return *  EFI_INVALID_PARAMETER\r
+  @return *  EFI_OUT_OF_RESOURCES\r
+  @return *  EFI_BAD_BUFFER_SIZE\r
+  @return *  Status is also returned from IpFilter(), TftpInfo(), MtftpDownload(),\r
+  @return *  TftpDownload() and TftpUpload().\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp (\r
+  PXE_BASECODE_DEVICE               *Private,\r
+  IN EFI_PXE_BASE_CODE_TFTP_OPCODE  Operation,\r
+  UINT64                            *BufferSizePtr,\r
+  VOID                              *BufferPtr,\r
+  EFI_IP_ADDRESS                    *ServerIpPtr,\r
+  UINT8                             *FilenamePtr,\r
+  UINTN                             *PacketSizePtr,\r
+  IN EFI_PXE_BASE_CODE_MTFTP_INFO   *MtftpInfoPtr, OPTIONAL\r
+  IN BOOLEAN                        Overwrite,\r
+  IN BOOLEAN                        DontUseBuffer\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_IP_FILTER Filter;\r
+  EFI_STATUS                  StatCode;\r
+  EFI_STATUS                  Status;\r
+  UINT64                      BufferSizeLocal;\r
+  UINTN                       PacketSize;\r
+  UINT8                       *BufferPtrLocal;\r
+\r
+  Filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+  Filter.IpCnt    = 0;\r
+  Filter.reserved = 0;\r
+\r
+  /* No error has occurred, yet. */\r
+  Private->EfiBc.Mode->TftpErrorReceived = FALSE;\r
+\r
+  /* We must at least have an MTFTP server IP address and\r
+   * a pointer to the buffer size.\r
+   */\r
+  if (!ServerIpPtr || !BufferSizePtr) {\r
+    DEBUG ((DEBUG_WARN, "\nPxeBcMtftp()  Exit #1"));\r
+\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private->Function = EFI_PXE_BASE_CODE_FUNCTION_MTFTP;\r
+\r
+  //\r
+  // make sure filter set to unicast at start\r
+  //\r
+  if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {\r
+    DEBUG (\r
+      (DEBUG_NET,\r
+      "\nPxeBcMtftp()  Exit  IpFilter() == %Xh",\r
+      StatCode)\r
+      );\r
+\r
+    return StatCode;\r
+  }\r
+  //\r
+  // set unset parms to default values\r
+  //\r
+  if (!PacketSizePtr) {\r
+    *(PacketSizePtr = &PacketSize) = MAX_TFTP_PKT_SIZE;\r
+  }\r
+\r
+  if (*PacketSizePtr > *BufferSizePtr) {\r
+    *PacketSizePtr = (UINTN) *BufferSizePtr;\r
+  }\r
+\r
+  if (*PacketSizePtr < MIN_TFTP_PKT_SIZE) {\r
+    *PacketSizePtr = MIN_TFTP_PKT_SIZE;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (*PacketSizePtr > BUFFER_ALLOCATE_SIZE) {\r
+    *PacketSizePtr = BUFFER_ALLOCATE_SIZE;\r
+  }\r
+\r
+  if (*PacketSizePtr > MAX_TFTP_PKT_SIZE) {\r
+    *PacketSizePtr = MAX_TFTP_PKT_SIZE;\r
+  }\r
+\r
+  if (Operation == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {\r
+    StatCode = TftpInfo (\r
+                Private,\r
+                BufferSizePtr,\r
+                ServerIpPtr,\r
+                TftpRequestPort,\r
+                FilenamePtr,\r
+                PacketSizePtr\r
+                );\r
+\r
+    if (StatCode != EFI_SUCCESS) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nPxeBcMtftp()  Exit  TftpInfo() == %Xh",\r
+        StatCode)\r
+        );\r
+    }\r
+\r
+    return StatCode;\r
+  }\r
+\r
+  if (Operation == EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE) {\r
+    if (!MtftpInfoPtr || !MtftpInfoPtr->SPort) {\r
+      DEBUG ((DEBUG_WARN, "\nPxeBcMtftp()  Exit #2"));\r
+      return EFI_INVALID_PARAMETER;\r
+    } else {\r
+      StatCode = TftpInfo (\r
+                  Private,\r
+                  BufferSizePtr,\r
+                  ServerIpPtr,\r
+                  MtftpInfoPtr->SPort,\r
+                  FilenamePtr,\r
+                  PacketSizePtr\r
+                  );\r
+\r
+      gBS->Stall (10000);\r
+\r
+      if (StatCode != EFI_SUCCESS) {\r
+        DEBUG (\r
+          (DEBUG_WARN,\r
+          "\nPxeBcMtftp()  Exit  TftpInfo() == %Xh",\r
+          StatCode)\r
+          );\r
+      }\r
+\r
+      return StatCode;\r
+    }\r
+  }\r
+\r
+  if (!BufferPtr && !DontUseBuffer) {\r
+    //\r
+    // if dontusebuffer is false and no buffer???\r
+    //\r
+    DEBUG ((DEBUG_WARN, "\nPxeBcMtftp()  Exit #3"));\r
+    //\r
+    // DontUseBuffer can be true only for read_file operation\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DontUseBuffer) {\r
+    Status = gBS->AllocatePool (\r
+                    EfiBootServicesData,\r
+                    BUFFER_ALLOCATE_SIZE,\r
+                    &BufferPtrLocal\r
+                    );\r
+\r
+    if (EFI_ERROR (Status) || BufferPtrLocal == NULL) {\r
+      DEBUG ((DEBUG_NET, "\nPxeBcMtftp()  Exit #4"));\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    BufferSizeLocal = BUFFER_ALLOCATE_SIZE;\r
+  } else {\r
+    if (!*BufferSizePtr && Operation != EFI_PXE_BASE_CODE_TFTP_WRITE_FILE) {\r
+      DEBUG ((DEBUG_WARN, "\nPxeBcMtftp()  Exit #5"));\r
+      return EFI_BAD_BUFFER_SIZE;\r
+    }\r
+\r
+    BufferPtrLocal  = BufferPtr;\r
+    BufferSizeLocal = *BufferSizePtr;\r
+  }\r
+\r
+  switch (Operation) {\r
+  case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:\r
+    if (FilenamePtr == NULL ||\r
+        MtftpInfoPtr == NULL ||\r
+        MtftpInfoPtr->MCastIp.Addr[0] == 0 ||\r
+        MtftpInfoPtr->SPort == 0 ||\r
+        MtftpInfoPtr->CPort == 0 ||\r
+        MtftpInfoPtr->ListenTimeout == 0 ||\r
+        MtftpInfoPtr->TransmitTimeout == 0\r
+        ) {\r
+      StatCode = EFI_INVALID_PARAMETER;\r
+      break;\r
+    }\r
+    //\r
+    // try MTFTP - if fails, drop into TFTP read\r
+    //\r
+    if ((StatCode = MtftpDownload (\r
+                      Private,\r
+                      &BufferSizeLocal,\r
+                      BufferPtrLocal,\r
+                      ServerIpPtr,\r
+                      FilenamePtr,\r
+                      MtftpInfoPtr,\r
+                      DontUseBuffer\r
+                      )) == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {\r
+      if (BufferSizePtr /* %% !DontUseBuffer */ ) {\r
+        *BufferSizePtr = BufferSizeLocal;\r
+      }\r
+\r
+      break;\r
+    }\r
+    //\r
+    // go back to unicast\r
+    //\r
+    if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {\r
+      break;\r
+    }\r
+\r
+  /* fall thru */\r
+  case EFI_PXE_BASE_CODE_TFTP_READ_FILE:\r
+    if (FilenamePtr == NULL) {\r
+      StatCode = EFI_INVALID_PARAMETER;\r
+      break;\r
+    }\r
+\r
+    StatCode = TftpDownload (\r
+                Private,\r
+                &BufferSizeLocal,\r
+                BufferPtrLocal,\r
+                ServerIpPtr,\r
+                FilenamePtr,\r
+                PacketSizePtr,\r
+                TftpRequestPort,\r
+                TFTP_RRQ,\r
+                DontUseBuffer\r
+                );\r
+\r
+    if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {\r
+      if (BufferSizePtr /* !DontUseBuffer */ ) {\r
+        *BufferSizePtr = BufferSizeLocal;\r
+      }\r
+    }\r
+\r
+    break;\r
+\r
+  case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:\r
+    if (FilenamePtr == NULL || DontUseBuffer) {\r
+      //\r
+      // not a valid option\r
+      //\r
+      StatCode = EFI_INVALID_PARAMETER;\r
+      break;\r
+    }\r
+\r
+    StatCode = TftpUpload (\r
+                Private,\r
+                BufferSizePtr,\r
+                BufferPtr,\r
+                ServerIpPtr,\r
+                FilenamePtr,\r
+                PacketSizePtr,\r
+                Overwrite\r
+                );\r
+\r
+    if (StatCode != EFI_SUCCESS) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nPxeBcMtftp()  Exit #6  %xh (%r)",\r
+        StatCode,\r
+        StatCode)\r
+        );\r
+    }\r
+\r
+    return StatCode;\r
+\r
+  case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:\r
+    if (FilenamePtr == NULL || DontUseBuffer) {\r
+      //\r
+      // not a valid option\r
+      //\r
+      StatCode = EFI_INVALID_PARAMETER;\r
+      break;\r
+    }\r
+\r
+    StatCode = TftpDownload (\r
+                Private,\r
+                BufferSizePtr,\r
+                BufferPtr,\r
+                ServerIpPtr,\r
+                FilenamePtr,\r
+                PacketSizePtr,\r
+                TftpRequestPort,\r
+                TFTP_DIR,\r
+                DontUseBuffer\r
+                );\r
+\r
+    if (StatCode != EFI_SUCCESS) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nPxeBcMtftp()  Exit #7  %xh (%r)",\r
+        StatCode,\r
+        StatCode)\r
+        );\r
+    }\r
+\r
+    return StatCode;\r
+\r
+  case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:\r
+    if (DontUseBuffer) {\r
+      StatCode = EFI_INVALID_PARAMETER;\r
+      break;\r
+    }\r
+\r
+    if (MtftpInfoPtr == NULL || !MtftpInfoPtr->SPort) {\r
+      DEBUG (\r
+        (DEBUG_WARN,\r
+        "\nPxeBcMtftp()  Exit #9  %xh (%r)",\r
+        EFI_INVALID_PARAMETER,\r
+        EFI_INVALID_PARAMETER)\r
+        );\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    StatCode = TftpDownload (\r
+                Private,\r
+                BufferSizePtr,\r
+                BufferPtr,\r
+                ServerIpPtr,\r
+                (UINT8 *) "/",\r
+                PacketSizePtr,\r
+                MtftpInfoPtr->SPort,\r
+                TFTP_DIR,\r
+                DontUseBuffer\r
+                );\r
+\r
+    break;\r
+\r
+  default:\r
+    StatCode = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DontUseBuffer) {\r
+    gBS->FreePool (BufferPtrLocal);\r
+  }\r
+\r
+  if (StatCode != EFI_SUCCESS) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nPxeBcMtftp()  Exit #8  %xh (%r)",\r
+      StatCode,\r
+      StatCode)\r
+      );\r
+  }\r
+\r
+  gBS->Stall (10000);\r
+\r
+  return StatCode;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return *  EFI_INVALID_PARAMETER\r
+  @return *  Status is also returned from PxeBcMtftp();\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcMtftp (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       * This,\r
+  IN EFI_PXE_BASE_CODE_TFTP_OPCODE    Operation,\r
+  IN OUT VOID                         *BufferPtr,\r
+  IN BOOLEAN                          Overwrite,\r
+  IN OUT UINT64                       *BufferSizePtr,\r
+  IN UINTN                            *BlockSizePtr OPTIONAL,\r
+  IN EFI_IP_ADDRESS                   * ServerIpPtr,\r
+  IN UINT8                            *FilenamePtr,\r
+  IN EFI_PXE_BASE_CODE_MTFTP_INFO     * MtftpInfoPtr OPTIONAL,\r
+  IN BOOLEAN                          DontUseBuffer\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_IP_FILTER Filter;\r
+  EFI_STATUS                  StatCode;\r
+  PXE_BASECODE_DEVICE         *Private;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!IS_INADDR_UNICAST (ServerIpPtr)) {\r
+      //\r
+      // The station IP is not a unicast address.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+  //\r
+  // Issue BC command\r
+  //\r
+  Filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+  Filter.IpCnt    = 0;\r
+  Filter.reserved = 0;\r
+\r
+  DEBUG ((DEBUG_WARN, "\nBcMtftp()  Op=%d  Buf=%Xh", Operation, BufferPtr));\r
+\r
+  StatCode = PxeBcMtftp (\r
+              Private,\r
+              Operation,\r
+              BufferSizePtr,\r
+              BufferPtr,\r
+              ServerIpPtr,\r
+              FilenamePtr,\r
+              BlockSizePtr,\r
+              MtftpInfoPtr,\r
+              Overwrite,\r
+              DontUseBuffer\r
+              );\r
+\r
+  //\r
+  // restore to unicast\r
+  //\r
+  IpFilter (Private, &Filter);\r
+\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+\r
+/* eof - PxeBcMtftp.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c
new file mode 100644 (file)
index 0000000..fcb55ac
--- /dev/null
@@ -0,0 +1,517 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  pxe_bc_udp.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "Bc.h"\r
+\r
+//\r
+// //////////////////////////////////////////////////////////////////////\r
+//\r
+//  Udp Write Routine - called by base code - e.g. TFTP - already locked\r
+//\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS :=\r
+  @return EFI_INVALID_PARAMETER :=\r
+  @return other :=\r
+\r
+**/\r
+EFI_STATUS\r
+UdpWrite (\r
+  IN PXE_BASECODE_DEVICE            *Private,\r
+  IN UINT16                         OpFlags,\r
+  IN EFI_IP_ADDRESS                 *DestIpPtr,\r
+  IN EFI_PXE_BASE_CODE_UDP_PORT     *DestPortPtr,\r
+  IN EFI_IP_ADDRESS                 *GatewayIpPtr, OPTIONAL\r
+  IN EFI_IP_ADDRESS                 *SrcIpPtr, OPTIONAL\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL\r
+  IN UINTN                          *HeaderSizePtr, OPTIONAL\r
+  IN VOID                           *HeaderPtr, OPTIONAL\r
+  IN UINTN                          *BufferSizeptr,\r
+  IN VOID                           *BufferPtr\r
+  )\r
+{\r
+  UINTN                       TotalLength;\r
+  UINTN                       HeaderSize;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  DefaultSrcPort;\r
+\r
+  //\r
+  //\r
+  //\r
+  HeaderSize      = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0;\r
+  DefaultSrcPort  = 0;\r
+\r
+  //\r
+  // check parameters\r
+  //\r
+  if (BufferSizeptr == NULL ||\r
+      BufferPtr == NULL ||\r
+      DestIpPtr == NULL ||\r
+      DestPortPtr == NULL ||\r
+      (HeaderSizePtr != NULL && *HeaderSizePtr == 0) ||\r
+      (HeaderSize != 0 && HeaderPtr == NULL) ||\r
+      (GatewayIpPtr != NULL && !IS_INADDR_UNICAST(GatewayIpPtr)) ||\r
+      (OpFlags &~(EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT))\r
+      ) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nUdpWrite()  Exit #1  %xh (%r)",\r
+      EFI_INVALID_PARAMETER,\r
+      EFI_INVALID_PARAMETER)\r
+      );\r
+\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  TotalLength = *BufferSizeptr + HeaderSize + sizeof (UDPV4_HEADER);\r
+\r
+  if (TotalLength > 0x0000ffff) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nUdpWrite()  Exit #2  %xh (%r)",\r
+      EFI_BAD_BUFFER_SIZE,\r
+      EFI_BAD_BUFFER_SIZE)\r
+      );\r
+\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  if (SrcIpPtr == NULL) {\r
+    SrcIpPtr = &Private->EfiBc.Mode->StationIp;\r
+  }\r
+\r
+  if (SrcPortPtr == NULL) {\r
+    SrcPortPtr = &DefaultSrcPort;\r
+    OpFlags |= EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT;\r
+  }\r
+\r
+  if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) {\r
+    *SrcPortPtr = Private->RandomPort;\r
+\r
+    if (++Private->RandomPort == 0) {\r
+      Private->RandomPort = PXE_RND_PORT_LOW;\r
+    }\r
+  }\r
+\r
+#define IpTxBuffer  ((IPV4_BUFFER *) Private->TransmitBufferPtr)\r
+  //\r
+  // build pseudo header and udp header in transmit buffer\r
+  //\r
+#define Udpv4Base ((UDPV4_HEADERS *) (IpTxBuffer->u.Data - sizeof (UDPV4_PSEUDO_HEADER)))\r
+\r
+  Udpv4Base->Udpv4PseudoHeader.SrcAddr.L    = SrcIpPtr->Addr[0];\r
+  Udpv4Base->Udpv4PseudoHeader.DestAddr.L   = DestIpPtr->Addr[0];\r
+  Udpv4Base->Udpv4PseudoHeader.Zero         = 0;\r
+  Udpv4Base->Udpv4PseudoHeader.Protocol     = PROT_UDP;\r
+  Udpv4Base->Udpv4PseudoHeader.TotalLength  = HTONS (TotalLength);\r
+  Udpv4Base->Udpv4Header.SrcPort            = HTONS (*SrcPortPtr);\r
+  Udpv4Base->Udpv4Header.DestPort           = HTONS (*DestPortPtr);\r
+  Udpv4Base->Udpv4Header.TotalLength        = Udpv4Base->Udpv4PseudoHeader.TotalLength;\r
+  Udpv4Base->Udpv4Header.Checksum           = 0;\r
+\r
+  if (HeaderSize != 0) {\r
+    CopyMem (IpTxBuffer->u.Udp.Data, HeaderPtr, HeaderSize);\r
+  }\r
+\r
+  HeaderSize += sizeof (UDPV4_HEADER);\r
+\r
+  Udpv4Base->Udpv4Header.Checksum = IpChecksum2 (\r
+                                      (UINT16 *) Udpv4Base,\r
+                                      HeaderSize + sizeof (UDPV4_PSEUDO_HEADER),\r
+                                      (UINT16 *) BufferPtr,\r
+                                      (UINT16) *BufferSizeptr\r
+                                      );\r
+\r
+  if (Udpv4Base->Udpv4Header.Checksum == 0) {\r
+    Udpv4Base->Udpv4Header.Checksum = 0xffff;\r
+    //\r
+    // transmit zero checksum as ones complement\r
+    //\r
+  }\r
+\r
+  return Ip4Send (\r
+          Private,\r
+          OpFlags,\r
+          PROT_UDP,\r
+          Udpv4Base->Udpv4PseudoHeader.SrcAddr.L,\r
+          Udpv4Base->Udpv4PseudoHeader.DestAddr.L,\r
+          (GatewayIpPtr) ? GatewayIpPtr->Addr[0] : 0,\r
+          HeaderSize,\r
+          BufferPtr,\r
+          *BufferSizeptr\r
+          );\r
+}\r
+//\r
+// //////////////////////////////////////////////////////////\r
+//\r
+//  BC Udp Write Routine\r
+//\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS :=\r
+  @return other :=\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcUdpWrite (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL     *This,\r
+  IN UINT16                         OpFlags,\r
+  IN EFI_IP_ADDRESS                 *DestIpPtr,\r
+  IN EFI_PXE_BASE_CODE_UDP_PORT     *DestPortPtr,\r
+  IN EFI_IP_ADDRESS                 *GatewayIpPtr, OPTIONAL\r
+  IN EFI_IP_ADDRESS                 *SrcIpPtr, OPTIONAL\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL\r
+  IN UINTN                          *HeaderSizePtr, OPTIONAL\r
+  IN VOID                           *HeaderPtr, OPTIONAL\r
+  IN UINTN                          *BufferSizeptr,\r
+  IN VOID                           *BufferPtr\r
+  )\r
+{\r
+  EFI_STATUS          StatCode;\r
+  PXE_BASECODE_DEVICE *Private;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_WRITE;\r
+\r
+  //\r
+  // Issue BC command\r
+  //\r
+  StatCode = UdpWrite (\r
+              Private,\r
+              OpFlags,\r
+              DestIpPtr,\r
+              DestPortPtr,\r
+              GatewayIpPtr,\r
+              SrcIpPtr,\r
+              SrcPortPtr,\r
+              HeaderSizePtr,\r
+              HeaderPtr,\r
+              BufferSizeptr,\r
+              BufferPtr\r
+              );\r
+\r
+  //\r
+  // Unlock the instance data\r
+  //\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+//\r
+// /////////////////////////////////////////////////////////////////////\r
+//\r
+//  Udp Read Routine - called by base code - e.g. TFTP - already locked\r
+//\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS :=\r
+  @return EFI_INVALID_PARAMETER :=\r
+  @return other :=\r
+\r
+**/\r
+EFI_STATUS\r
+UdpRead (\r
+  IN PXE_BASECODE_DEVICE            *Private,\r
+  IN UINT16                         OpFlags,\r
+  IN OUT EFI_IP_ADDRESS             *DestIpPtr, OPTIONAL\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, OPTIONAL\r
+  IN OUT EFI_IP_ADDRESS             *SrcIpPtr, OPTIONAL\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL\r
+  IN UINTN                          *HeaderSizePtr, OPTIONAL\r
+  IN VOID                           *HeaderPtr, OPTIONAL\r
+  IN OUT UINTN                      *BufferSizeptr,\r
+  IN VOID                           *BufferPtr,\r
+  EFI_EVENT                         TimeoutEvent\r
+  )\r
+{\r
+  EFI_STATUS      StatCode;\r
+  EFI_IP_ADDRESS  TmpSrcIp;\r
+  EFI_IP_ADDRESS  TmpDestIp;\r
+  UINTN           BufferSize;\r
+  UINTN           HeaderSize;\r
+\r
+  //\r
+  // combination structure of pseudo header/udp header\r
+  //\r
+#pragma pack (1)\r
+  struct {\r
+    UDPV4_PSEUDO_HEADER Udpv4PseudoHeader;\r
+    UDPV4_HEADER        Udpv4Header;\r
+    UINT8               ProtHdr[64];\r
+  } Hdrs;\r
+#pragma pack ()\r
+\r
+  HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0;\r
+  //\r
+  // read [with filtering]\r
+  // check parameters\r
+  //\r
+  if (BufferSizeptr == NULL ||\r
+      BufferPtr == NULL ||\r
+      (HeaderSize != 0 && HeaderPtr == NULL) ||\r
+      (OpFlags &~UDP_FILTER_MASK)\r
+      //\r
+      // if filtering on a particular IP/Port, need it\r
+      //\r
+      ||\r
+      (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr == NULL) ||\r
+      (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && SrcPortPtr == NULL) ||\r
+      (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && DestPortPtr == NULL)\r
+      ) {\r
+    DEBUG ((DEBUG_INFO, "\nUdpRead()  Exit #1  Invalid Parameter"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // in case we loop\r
+  //\r
+  BufferSize = *BufferSizeptr;\r
+  //\r
+  // we need source and dest IPs for pseudo header\r
+  //\r
+  if (SrcIpPtr == NULL) {\r
+    SrcIpPtr = &TmpSrcIp;\r
+  }\r
+\r
+  if (DestIpPtr == NULL) {\r
+    DestIpPtr = &TmpDestIp;\r
+    TmpDestIp = Private->EfiBc.Mode->StationIp;\r
+  }\r
+\r
+#if SUPPORT_IPV6\r
+  if (Private->EfiBc.Mode->UsingIpv6) {\r
+    //\r
+    // %%TBD\r
+    //\r
+  }\r
+#endif\r
+\r
+  for (;;) {\r
+    *BufferSizeptr = BufferSize;\r
+\r
+    StatCode = IpReceive (\r
+                Private,\r
+                OpFlags,\r
+                SrcIpPtr,\r
+                DestIpPtr,\r
+                PROT_UDP,\r
+                &Hdrs.Udpv4Header,\r
+                HeaderSize + sizeof Hdrs.Udpv4Header,\r
+                BufferPtr,\r
+                BufferSizeptr,\r
+                TimeoutEvent\r
+                );\r
+\r
+    if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {\r
+      UINT16  SPort;\r
+      UINT16  DPort;\r
+\r
+      SPort = NTOHS (Hdrs.Udpv4Header.SrcPort);\r
+      DPort = NTOHS (Hdrs.Udpv4Header.DestPort);\r
+\r
+      //\r
+      // do filtering\r
+      //\r
+      if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && *SrcPortPtr != SPort) {\r
+        continue;\r
+      }\r
+\r
+      if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && *DestPortPtr != DPort) {\r
+        continue;\r
+      }\r
+      //\r
+      // check checksum\r
+      //\r
+      if (StatCode == EFI_SUCCESS && Hdrs.Udpv4Header.Checksum) {\r
+        Hdrs.Udpv4PseudoHeader.SrcAddr.L    = SrcIpPtr->Addr[0];\r
+        Hdrs.Udpv4PseudoHeader.DestAddr.L   = DestIpPtr->Addr[0];\r
+        Hdrs.Udpv4PseudoHeader.Zero         = 0;\r
+        Hdrs.Udpv4PseudoHeader.Protocol     = PROT_UDP;\r
+        Hdrs.Udpv4PseudoHeader.TotalLength  = Hdrs.Udpv4Header.TotalLength;\r
+\r
+        if (Hdrs.Udpv4Header.Checksum == 0xffff) {\r
+          Hdrs.Udpv4Header.Checksum = 0;\r
+        }\r
+\r
+        if (IpChecksum2 (\r
+              (UINT16 *) &Hdrs.Udpv4PseudoHeader,\r
+              HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader) + sizeof (Hdrs.Udpv4Header),\r
+              (UINT16 *) BufferPtr,\r
+              *BufferSizeptr\r
+              )) {\r
+          DEBUG (\r
+            (DEBUG_INFO,\r
+            "\nUdpRead()  Hdrs.Udpv4PseudoHeader == %Xh",\r
+            Hdrs.Udpv4PseudoHeader)\r
+            );\r
+          DEBUG (\r
+            (DEBUG_INFO,\r
+            "\nUdpRead()  Header size == %d",\r
+            HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader))\r
+            );\r
+          DEBUG (\r
+            (DEBUG_INFO,\r
+            "\nUdpRead()  BufferPtr == %Xh",\r
+            BufferPtr)\r
+            );\r
+          DEBUG (\r
+            (DEBUG_INFO,\r
+            "\nUdpRead()  Buffer size == %d",\r
+            *BufferSizeptr)\r
+            );\r
+          DEBUG ((DEBUG_INFO, "\nUdpRead()  Exit #2  Device Error"));\r
+          return EFI_DEVICE_ERROR;\r
+        }\r
+      }\r
+      //\r
+      // all passed\r
+      //\r
+      if (SrcPortPtr != NULL) {\r
+        *SrcPortPtr = SPort;\r
+      }\r
+\r
+      if (DestPortPtr != NULL) {\r
+        *DestPortPtr = DPort;\r
+      }\r
+\r
+      if (HeaderSize != 0) {\r
+        CopyMem (HeaderPtr, Hdrs.ProtHdr, HeaderSize);\r
+      }\r
+    }\r
+\r
+    if ((StatCode != EFI_SUCCESS) && (StatCode != EFI_TIMEOUT)) {\r
+      DEBUG (\r
+        (DEBUG_INFO,\r
+        "\nUdpRead()  Exit #3  %Xh %r",\r
+        StatCode,\r
+        StatCode)\r
+        );\r
+    }\r
+\r
+    return StatCode;\r
+  }\r
+}\r
+//\r
+// //////////////////////////////////////////////////////////\r
+//\r
+//  BC Udp Read Routine\r
+//\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS :=\r
+  @return other :=\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BcUdpRead (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL     *This,\r
+  IN UINT16                         OpFlags,\r
+  IN OUT EFI_IP_ADDRESS             *DestIp, OPTIONAL\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL\r
+  IN OUT EFI_IP_ADDRESS             *SrcIp, OPTIONAL\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL\r
+  IN UINTN                          *HeaderSize, OPTIONAL\r
+  IN VOID                           *HeaderPtr, OPTIONAL\r
+  IN OUT UINTN                      *BufferSize,\r
+  IN VOID                           *BufferPtr\r
+  )\r
+{\r
+  EFI_STATUS          StatCode;\r
+  PXE_BASECODE_DEVICE *Private;\r
+\r
+  //\r
+  // Lock the instance data and make sure started\r
+  //\r
+  StatCode = EFI_SUCCESS;\r
+\r
+  if (This == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiAcquireLock (&Private->Lock);\r
+\r
+  if (This->Mode == NULL || !This->Mode->Started) {\r
+    DEBUG ((DEBUG_ERROR, "BC was not started."));\r
+    EfiReleaseLock (&Private->Lock);\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_READ;\r
+\r
+  //\r
+  // Issue BC command\r
+  //\r
+  StatCode = UdpRead (\r
+              Private,\r
+              OpFlags,\r
+              DestIp,\r
+              DestPort,\r
+              SrcIp,\r
+              SrcPort,\r
+              HeaderSize,\r
+              HeaderPtr,\r
+              BufferSize,\r
+              BufferPtr,\r
+              0\r
+              );\r
+\r
+  //\r
+  // Unlock the instance data and return\r
+  //\r
+  EfiReleaseLock (&Private->Lock);\r
+  return StatCode;\r
+}\r
+\r
+/* eof - pxe_bc_udp.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c
new file mode 100644 (file)
index 0000000..7c5d0bc
--- /dev/null
@@ -0,0 +1,1614 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  pxe_loadfile.c\r
+\r
+Abstract:\r
+  An implementation of the load file protocol for network devices.\r
+\r
+\r
+**/\r
+\r
+\r
+#include "Bc.h"\r
+\r
+#define DO_MENU     (EFI_SUCCESS)\r
+#define NO_MENU     (DO_MENU + 1)\r
+#define LOCAL_BOOT  (EFI_ABORTED)\r
+#define AUTO_SELECT (NO_MENU)\r
+\r
+#define NUMBER_ROWS   25  // we set to mode 0\r
+#define MAX_MENULIST  23\r
+\r
+#define Ctl(x)  (0x1F & (x))\r
+\r
+typedef union {\r
+  DHCPV4_OP_STRUCT          *OpPtr;\r
+  PXE_BOOT_MENU_ENTRY       *CurrentMenuItemPtr;\r
+  PXE_OP_DISCOVERY_CONTROL  *DiscCtlOpStr;\r
+  PXE_OP_BOOT_MENU          *MenuPtr;\r
+  UINT8                     *BytePtr;\r
+} UNION_PTR;\r
+\r
+\r
+\r
+/**\r
+  PxeBc callback routine for status updates and aborts.\r
+\r
+  @param  This                                        Pointer to PxeBcCallback\r
+                                                      interface\r
+  @param  Function                                    PxeBc function ID#\r
+  @param  Received                                    Receive/transmit flag\r
+  @param  PacketLength                                Length of received packet (0\r
+                                                      == idle callback)\r
+  @param  PacketPtr                                   Pointer to received packet\r
+                                                      (NULL == idle callback)\r
+\r
+  @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE\r
+                                                      EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT\r
+                                                      -\r
+\r
+**/\r
+STATIC\r
+EFI_PXE_BASE_CODE_CALLBACK_STATUS\r
+EFIAPI\r
+bc_callback (\r
+  IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  * This,\r
+  IN EFI_PXE_BASE_CODE_FUNCTION           Function,\r
+  IN BOOLEAN                              Received,\r
+  IN UINT32                               PacketLength,\r
+  IN EFI_PXE_BASE_CODE_PACKET             * PacketPtr OPTIONAL\r
+  )\r
+{\r
+  STATIC UINTN  Propeller;\r
+\r
+  EFI_INPUT_KEY Key;\r
+  UINTN         Row;\r
+  UINTN         Col;\r
+\r
+  Propeller = 0;\r
+  //\r
+  // Resolve Warning 4 unreferenced parameter problem\r
+  //\r
+  This = This;\r
+\r
+  //\r
+  // Check for user abort.\r
+  //\r
+  if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_SUCCESS) {\r
+    if (!Key.ScanCode) {\r
+      if (Key.UnicodeChar == Ctl ('c')) {\r
+        return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;\r
+      }\r
+    } else if (Key.ScanCode == SCAN_ESC) {\r
+      return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;\r
+    }\r
+  }\r
+  //\r
+  // Do nothing if this is a receive.\r
+  //\r
+  if (Received) {\r
+    return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+  }\r
+  //\r
+  // The display code is only for these functions.\r
+  //\r
+  switch (Function) {\r
+  case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:\r
+    //\r
+    // If this is a transmit and not a M/TFTP open request,\r
+    // return now.  Do not print a dot for each M/TFTP packet\r
+    // that is sent, only for the open packets.\r
+    //\r
+    if (PacketLength != 0 && PacketPtr != NULL) {\r
+      if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {\r
+        return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+      }\r
+    }\r
+\r
+    break;\r
+\r
+  case EFI_PXE_BASE_CODE_FUNCTION_DHCP:\r
+  case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:\r
+    break;\r
+\r
+  default:\r
+    return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+  }\r
+  //\r
+  // Display routines\r
+  //\r
+  if (PacketLength != 0 && PacketPtr != NULL) {\r
+    //\r
+    // Display a '.' when a packet is transmitted.\r
+    //\r
+    AsciiPrint (".");\r
+  } else if (PacketLength == 0 && PacketPtr == NULL) {\r
+    //\r
+    // Display a propeller when waiting for packets if at\r
+    // least 200 ms have passed.\r
+    //\r
+    Row = gST->ConOut->Mode->CursorRow;\r
+    Col = gST->ConOut->Mode->CursorColumn;\r
+\r
+    AsciiPrint ("%c", "/-\\|"[Propeller]);\r
+    gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row);\r
+\r
+    Propeller = (Propeller + 1) & 3;\r
+  }\r
+\r
+  return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+}\r
+\r
+STATIC EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  _bc_callback = {\r
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,\r
+  &bc_callback\r
+};\r
+\r
+\r
+/**\r
+  Display an IPv4 address in dot notation.\r
+\r
+  @param  Ptr                                         Pointer to IPv4 address.\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+PrintIpv4 (\r
+  UINT8 *Ptr\r
+  )\r
+{\r
+  if (Ptr != NULL) {\r
+    AsciiPrint ("%d.%d.%d.%d", Ptr[0], Ptr[1], Ptr[2], Ptr[3]);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Display client and server IP information.\r
+\r
+  @param  Private                                     Pointer to PxeBc interface\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+ShowMyInfo (\r
+  IN PXE_BASECODE_DEVICE *Private\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
+  UINTN                   Index;\r
+\r
+  //\r
+  // Do nothing if a NULL pointer is passed in.\r
+  //\r
+  if (Private == NULL) {\r
+    return ;\r
+  }\r
+  //\r
+  // Get pointer to PXE BaseCode mode structure\r
+  //\r
+  PxeBcMode = Private->EfiBc.Mode;\r
+\r
+  //\r
+  // Display client IP address\r
+  //\r
+  AsciiPrint ("\rCLIENT IP: ");\r
+  PrintIpv4 (PxeBcMode->StationIp.v4.Addr);\r
+\r
+  //\r
+  // Display subnet mask\r
+  //\r
+  AsciiPrint ("  MASK: ");\r
+  PrintIpv4 (PxeBcMode->SubnetMask.v4.Addr);\r
+\r
+  //\r
+  // Display DHCP and proxyDHCP IP addresses\r
+  //\r
+  if (PxeBcMode->ProxyOfferReceived) {\r
+    AsciiPrint ("\nDHCP IP: ");\r
+    PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);\r
+\r
+    AsciiPrint ("  PROXY IP: ");\r
+    PrintIpv4 (((DHCPV4_OP_SERVER_IP *) PXE_OFFER_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);\r
+  } else {\r
+    AsciiPrint ("  DHCP IP: ");\r
+    PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);\r
+  }\r
+  //\r
+  // Display gateway IP addresses\r
+  //\r
+  for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {\r
+    if ((Index % 3) == 0) {\r
+      AsciiPrint ("\r\nGATEWAY IP:");\r
+    }\r
+\r
+    AsciiPrint (" ");\r
+    PrintIpv4 (PxeBcMode->RouteTable[Index].GwAddr.v4.Addr);\r
+    AsciiPrint (" ");\r
+  }\r
+\r
+  AsciiPrint ("\n");\r
+}\r
+\r
+\r
+/**\r
+  Display prompt and wait for input.\r
+\r
+  @param  Private                                     Pointer to PxeBc interface\r
+  @param  BootPromptPtr                               Pointer to PXE boot prompt\r
+                                                      option\r
+\r
+  @retval AUTO_SELECT                                 DO_MENU -\r
+  @retval NO_MENU\r
+  @retval LOCAL_BOOT\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DoPrompt (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  PXE_OP_BOOT_PROMPT  *BootPromptPtr\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  EFI_EVENT   TimeoutEvent;\r
+  EFI_EVENT   SecondsEvent;\r
+  INT32       SecColumn;\r
+  INT32       SecRow;\r
+  UINT8       SaveChar;\r
+  UINT8       SecsLeft;\r
+\r
+  //\r
+  // if auto select, just get right to it\r
+  //\r
+  if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_AUTO_SELECT) {\r
+    return AUTO_SELECT;\r
+  }\r
+  //\r
+  // if no timeout, go directly to display of menu\r
+  //\r
+  if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_NO_TIMEOUT) {\r
+    return DO_MENU;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &TimeoutEvent\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return DO_MENU;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  TimeoutEvent,\r
+                  TimerRelative,\r
+                  BootPromptPtr->Timeout * 10000000 + 100000\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return DO_MENU;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &SecondsEvent\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return DO_MENU;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  SecondsEvent,\r
+                  TimerPeriodic,\r
+                  10000000\r
+                  );  /* 1 second */\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseEvent (SecondsEvent);\r
+    gBS->CloseEvent (TimeoutEvent);\r
+    return DO_MENU;\r
+  }\r
+  //\r
+  // display the prompt\r
+  // IMPORTANT!  This prompt is an ASCII character string that may\r
+  // not be terminated with a NULL byte.\r
+  //\r
+  SaveChar  = BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1];\r
+  BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = 0;\r
+\r
+  AsciiPrint ("%a ", BootPromptPtr->Prompt);\r
+  BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = SaveChar;\r
+\r
+  //\r
+  // wait until time expires or selection made - menu or local\r
+  //\r
+  SecColumn = gST->ConOut->Mode->CursorColumn;\r
+  SecRow    = gST->ConOut->Mode->CursorRow;\r
+  SecsLeft  = BootPromptPtr->Timeout;\r
+\r
+  gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);\r
+  AsciiPrint ("(%d) ", SecsLeft);\r
+\r
+  //\r
+  // set the default action to be AUTO_SELECT\r
+  //\r
+  Status = AUTO_SELECT;\r
+\r
+  while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+    EFI_INPUT_KEY Key;\r
+\r
+    if (!EFI_ERROR (gBS->CheckEvent (SecondsEvent))) {\r
+      --SecsLeft;\r
+      gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);\r
+      AsciiPrint ("(%d) ", SecsLeft);\r
+    }\r
+\r
+    if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {\r
+      UINT8       Buffer[512];\r
+      UINTN       BufferSize;\r
+      EFI_STATUS  Status;\r
+\r
+      BufferSize = sizeof Buffer;\r
+\r
+      Status = Private->EfiBc.UdpRead (\r
+                                &Private->EfiBc,\r
+                                EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |\r
+                                EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |\r
+                                EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,\r
+                                NULL, /* dest ip */\r
+                                NULL, /* dest port */\r
+                                NULL, /* src ip */\r
+                                NULL, /* src port */\r
+                                NULL, /* hdr size */\r
+                                NULL, /* hdr ptr */\r
+                                &BufferSize,\r
+                                Buffer\r
+                                );\r
+\r
+      continue;\r
+    }\r
+\r
+    if (Key.ScanCode == 0) {\r
+      switch (Key.UnicodeChar) {\r
+      case Ctl ('c'):\r
+        Status = LOCAL_BOOT;\r
+        break;\r
+\r
+      case Ctl ('m'):\r
+      case 'm':\r
+      case 'M':\r
+        Status = DO_MENU;\r
+        break;\r
+\r
+      default:\r
+        continue;\r
+      }\r
+    } else {\r
+      switch (Key.ScanCode) {\r
+      case SCAN_F8:\r
+        Status = DO_MENU;\r
+        break;\r
+\r
+      case SCAN_ESC:\r
+        Status = LOCAL_BOOT;\r
+        break;\r
+\r
+      default:\r
+        continue;\r
+      }\r
+    }\r
+\r
+    break;\r
+  }\r
+\r
+  gBS->CloseEvent (SecondsEvent);\r
+  gBS->CloseEvent (TimeoutEvent);\r
+\r
+  gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);\r
+  AsciiPrint ("     ");\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Display one menu item.\r
+\r
+  @param  MenuItemPtr                                 Pointer to PXE menu item\r
+                                                      option.\r
+\r
+  @return None\r
+\r
+**/\r
+STATIC\r
+VOID\r
+PrintMenuItem (\r
+  PXE_BOOT_MENU_ENTRY *MenuItemPtr\r
+  )\r
+{\r
+  UINT8 Length;\r
+  UINT8 SaveChar;\r
+\r
+  Length                    = (UINT8) MIN (70, MenuItemPtr->DataLen);\r
+  SaveChar                  = MenuItemPtr->Data[Length];\r
+\r
+  MenuItemPtr->Data[Length] = 0;\r
+  AsciiPrint ("     %a\n", MenuItemPtr->Data);\r
+  MenuItemPtr->Data[Length] = SaveChar;\r
+}\r
+\r
+\r
+/**\r
+  Display and process menu.\r
+\r
+  @param  Private                                     Pointer to PxeBc interface\r
+  @param  RxBufferPtr                                 Pointer to receive buffer\r
+\r
+  @retval NO_MENU\r
+  @retval LOCAL_BOOT\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DoMenu (\r
+  PXE_BASECODE_DEVICE *Private,\r
+  DHCP_RECEIVE_BUFFER *RxBufferPtr\r
+  )\r
+{\r
+  PXE_OP_DISCOVERY_CONTROL  *DiscoveryControlPtr;\r
+  PXE_BOOT_MENU_ENTRY       *MenuItemPtrs[MAX_MENULIST];\r
+  EFI_STATUS                Status;\r
+  UNION_PTR                 Ptr;\r
+  UINTN                     SaveNumRte;\r
+  UINTN                     TopRow;\r
+  UINTN                     MenuLth;\r
+  UINTN                     NumMenuItems;\r
+  UINTN                     Index;\r
+  UINTN                     Longest;\r
+  UINTN                     Selected;\r
+  UINT16                    Type;\r
+  UINT16                    Layer;\r
+  BOOLEAN                   Done;\r
+\r
+  Selected  = 0;\r
+  Layer     = 0;\r
+\r
+  DEBUG ((DEBUG_WARN, "\nDoMenu()  Enter."));\r
+\r
+  /* see if we have a menu/prompt */\r
+  if (!(RxBufferPtr->OpAdds.Status & DISCOVER_TYPE)) {\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nDoMenu()  No menu/prompt info.  OpAdds.Status == %xh  ",\r
+      RxBufferPtr->OpAdds.Status)\r
+      );\r
+\r
+    return NO_MENU;\r
+  }\r
+\r
+  DiscoveryControlPtr = (PXE_OP_DISCOVERY_CONTROL *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];\r
+\r
+  //\r
+  // if not USE_BOOTFILE or no bootfile given, must have menu stuff\r
+  //\r
+  if ((DiscoveryControlPtr->ControlBits & USE_BOOTFILE) && RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {\r
+    DEBUG ((DEBUG_WARN, "\nDoMenu()  DHCP w/ bootfile.  "));\r
+    return NO_MENU;\r
+  }\r
+  //\r
+  // do prompt & menu if necessary\r
+  //\r
+  Status = DoPrompt (Private, (PXE_OP_BOOT_PROMPT *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]);\r
+\r
+  if (Status == LOCAL_BOOT) {\r
+    DEBUG ((DEBUG_WARN, "\nDoMenu()  DoPrompt() returned LOCAL_BOOT.  "));\r
+\r
+    return Status;\r
+  }\r
+\r
+  Ptr.BytePtr             = (UINT8 *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];\r
+\r
+  MenuLth                 = Ptr.MenuPtr->Header.Length;\r
+  Ptr.CurrentMenuItemPtr  = Ptr.MenuPtr->MenuItem;\r
+\r
+  //\r
+  // build menu items array\r
+  //\r
+  for (Longest = NumMenuItems = Index = 0; Index < MenuLth && NumMenuItems < MAX_MENULIST;) {\r
+    UINTN lth;\r
+\r
+    lth = Ptr.CurrentMenuItemPtr->DataLen + sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data);\r
+\r
+    MenuItemPtrs[NumMenuItems++] = Ptr.CurrentMenuItemPtr;\r
+\r
+    if (lth > Longest) {\r
+      //\r
+      // check if too long\r
+      //\r
+      if ((Longest = lth) > 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))) {\r
+        Longest = 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data));\r
+      }\r
+    }\r
+\r
+    Index += lth;\r
+    Ptr.BytePtr += lth;\r
+  }\r
+\r
+  if (Status != AUTO_SELECT) {\r
+    UINT8 BlankBuf[75];\r
+\r
+    SetMem (BlankBuf, sizeof BlankBuf, ' ');\r
+    BlankBuf[Longest + 5 - (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))] = 0;\r
+    AsciiPrint ("\n");\r
+\r
+    //\r
+    // now put up menu\r
+    //\r
+    for (Index = 0; Index < NumMenuItems; ++Index) {\r
+      PrintMenuItem (MenuItemPtrs[Index]);\r
+    }\r
+\r
+    TopRow = gST->ConOut->Mode->CursorRow - NumMenuItems;\r
+\r
+    //\r
+    // now wait for a selection\r
+    //\r
+    Done = FALSE;\r
+    do {\r
+      //\r
+      // highlight selection\r
+      //\r
+      EFI_INPUT_KEY Key;\r
+      UINTN         NewSelected;\r
+\r
+      NewSelected = Selected;\r
+\r
+      //\r
+      // highlight selected row\r
+      //\r
+      gST->ConOut->SetAttribute (\r
+                    gST->ConOut,\r
+                    EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)\r
+                    );\r
+      gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Selected);\r
+\r
+      AsciiPrint (" --->%a\r", BlankBuf);\r
+\r
+      PrintMenuItem (MenuItemPtrs[Selected]);\r
+      gST->ConOut->SetAttribute (\r
+                    gST->ConOut,\r
+                    EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)\r
+                    );\r
+      gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + NumMenuItems);\r
+\r
+      //\r
+      // wait for a keystroke\r
+      //\r
+      while (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {\r
+        UINT8 TmpBuf[512];\r
+        UINTN TmpBufLen;\r
+\r
+        TmpBufLen = sizeof TmpBuf;\r
+\r
+        Private->EfiBc.UdpRead (\r
+                        &Private->EfiBc,\r
+                        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |\r
+                        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |\r
+                        EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,\r
+                        NULL, /* dest ip */\r
+                        NULL, /* dest port */\r
+                        NULL, /* src ip */\r
+                        NULL, /* src port */\r
+                        NULL, /* hdr size */\r
+                        NULL, /* hdr ptr */\r
+                        &TmpBufLen,\r
+                        TmpBuf\r
+                        );\r
+      }\r
+\r
+      if (!Key.ScanCode) {\r
+        switch (Key.UnicodeChar) {\r
+        case Ctl ('c'):\r
+          Key.ScanCode = SCAN_ESC;\r
+          break;\r
+\r
+        case Ctl ('j'): /* linefeed */\r
+        case Ctl ('m'): /* return */\r
+          Done = TRUE;\r
+          break;\r
+\r
+        case Ctl ('i'): /* tab */\r
+        case ' ':\r
+        case 'd':\r
+        case 'D':\r
+          Key.ScanCode = SCAN_DOWN;\r
+          break;\r
+\r
+        case Ctl ('h'): /* backspace */\r
+        case 'u':\r
+        case 'U':\r
+          Key.ScanCode = SCAN_UP;\r
+          break;\r
+\r
+        default:\r
+          Key.ScanCode = 0;\r
+        }\r
+      }\r
+\r
+      switch (Key.ScanCode) {\r
+      case SCAN_LEFT:\r
+      case SCAN_UP:\r
+        if (NewSelected) {\r
+          --NewSelected;\r
+        }\r
+\r
+        break;\r
+\r
+      case SCAN_DOWN:\r
+      case SCAN_RIGHT:\r
+        if (++NewSelected == NumMenuItems) {\r
+          --NewSelected;\r
+        }\r
+\r
+        break;\r
+\r
+      case SCAN_PAGE_UP:\r
+      case SCAN_HOME:\r
+        NewSelected = 0;\r
+        break;\r
+\r
+      case SCAN_PAGE_DOWN:\r
+      case SCAN_END:\r
+        NewSelected = NumMenuItems - 1;\r
+        break;\r
+\r
+      case SCAN_ESC:\r
+        return LOCAL_BOOT;\r
+      }\r
+\r
+      /* unhighlight last selected row */\r
+      gST->ConOut->SetCursorPosition (gST->ConOut, 5, TopRow + Selected);\r
+\r
+      AsciiPrint ("%a\r", BlankBuf);\r
+\r
+      PrintMenuItem (MenuItemPtrs[Selected]);\r
+\r
+      Selected = NewSelected;\r
+    } while (!Done);\r
+  }\r
+\r
+  SaveNumRte  = Private->EfiBc.Mode->RouteTableEntries;\r
+\r
+  Type        = NTOHS (MenuItemPtrs[Selected]->Type);\r
+\r
+  if (Type == 0) {\r
+    DEBUG ((DEBUG_WARN, "\nDoMenu()  Local boot selected.  "));\r
+    return LOCAL_BOOT;\r
+  }\r
+\r
+  AsciiPrint ("Discover");\r
+\r
+  Status = Private->EfiBc.Discover (\r
+                            &Private->EfiBc,\r
+                            Type,\r
+                            &Layer,\r
+                            (BOOLEAN) (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected),\r
+                            0\r
+                            );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    AsciiPrint ("\r                    \r");\r
+\r
+    DEBUG (\r
+      (DEBUG_WARN,\r
+      "\nDoMenu()  Return w/ %xh (%r).",\r
+      Status,\r
+      Status)\r
+      );\r
+\r
+    return Status;\r
+  }\r
+\r
+  AsciiPrint ("\rBOOT_SERVER_IP: ");\r
+  PrintIpv4 ((UINT8 *) &Private->ServerIp);\r
+\r
+  for (Index = SaveNumRte; Index < Private->EfiBc.Mode->RouteTableEntries; ++Index) {\r
+    if ((Index % 3) == 0) {\r
+      AsciiPrint ("\r\nGATEWAY IP:");\r
+    }\r
+\r
+    AsciiPrint (" ");\r
+    PrintIpv4 ((UINT8 *) &Private->EfiBc.Mode->RouteTable[Index].GwAddr);\r
+    AsciiPrint (" ");\r
+  }\r
+\r
+  AsciiPrint ("\n");\r
+\r
+  DEBUG ((DEBUG_WARN, "\nDoMenu()  Return w/ EFI_SUCCESS.  "));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Get value 8- or 16-bit value from DHCP option.\r
+\r
+  @param  OpPtr                                       Pointer to DHCP option\r
+\r
+  @return Value from DHCP option\r
+\r
+**/\r
+STATIC\r
+UINT16\r
+GetValue (\r
+  DHCPV4_OP_STRUCT *OpPtr\r
+  )\r
+{\r
+  if (OpPtr->Header.Length == 1) {\r
+    return OpPtr->Data[0];\r
+  } else {\r
+    return NTOHS (OpPtr->Data);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Locate opcode in buffer.\r
+\r
+  @param  BufferPtr                                   Pointer to buffer\r
+  @param  BufferLen                                   Length of buffer\r
+  @param  OpCode                                      Option number\r
+\r
+  @return Pointer to opcode, may be NULL\r
+\r
+**/\r
+STATIC\r
+UINT8 *\r
+_PxeBcFindOpt (\r
+  UINT8 *BufferPtr,\r
+  UINTN BufferLen,\r
+  UINT8 OpCode\r
+  )\r
+{\r
+  if (BufferPtr == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  while (BufferLen != 0) {\r
+    if (*BufferPtr == OpCode) {\r
+      return BufferPtr;\r
+    }\r
+\r
+    switch (*BufferPtr) {\r
+    case OP_END:\r
+      return NULL;\r
+\r
+    case OP_PAD:\r
+      ++BufferPtr;\r
+      --BufferLen;\r
+      continue;\r
+    }\r
+\r
+    if ((UINTN) BufferLen <= (UINTN) 2 + BufferPtr[1]) {\r
+      return NULL;\r
+    }\r
+\r
+    BufferLen -= 2 + BufferPtr[1];\r
+    BufferPtr += 2 + BufferPtr[1];\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Find option in packet\r
+\r
+  @param  PacketPtr                                   Pointer to packet\r
+  @param  OpCode                                      option number\r
+\r
+  @return Pointer to option in packet\r
+\r
+**/\r
+STATIC\r
+UINT8 *\r
+PxeBcFindDhcpOpt (\r
+  EFI_PXE_BASE_CODE_PACKET  *PacketPtr,\r
+  UINT8                     OpCode\r
+  )\r
+{\r
+  UINTN PacketLen;\r
+  UINT8 Overload;\r
+  UINT8 *OptionBufferPtr;\r
+\r
+  //\r
+  //\r
+  //\r
+  PacketLen = 380;\r
+  Overload  = 0;\r
+\r
+  //\r
+  // Figure size of DHCP option space.\r
+  //\r
+  OptionBufferPtr = _PxeBcFindOpt (\r
+                      PacketPtr->Dhcpv4.DhcpOptions,\r
+                      380,\r
+                      OP_DHCP_MAX_MESSAGE_SZ\r
+                      );\r
+\r
+  if (OptionBufferPtr != NULL) {\r
+    if (OptionBufferPtr[1] == 2) {\r
+      UINT16  n;\r
+\r
+      CopyMem (&n, &OptionBufferPtr[2], 2);\r
+      PacketLen = HTONS (n);\r
+\r
+      if (PacketLen < sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET)) {\r
+        PacketLen = 380;\r
+      } else {\r
+        PacketLen -= (PacketPtr->Dhcpv4.DhcpOptions - &PacketPtr->Dhcpv4.BootpOpcode) + 28;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // Look for option overloading.\r
+  //\r
+  OptionBufferPtr = _PxeBcFindOpt (\r
+                      PacketPtr->Dhcpv4.DhcpOptions,\r
+                      PacketLen,\r
+                      OP_DHCP_OPTION_OVERLOAD\r
+                      );\r
+\r
+  if (OptionBufferPtr != NULL) {\r
+    if (OptionBufferPtr[1] == 1) {\r
+      Overload = OptionBufferPtr[2];\r
+    }\r
+  }\r
+  //\r
+  // Look for caller's option.\r
+  //\r
+  OptionBufferPtr = _PxeBcFindOpt (\r
+                      PacketPtr->Dhcpv4.DhcpOptions,\r
+                      PacketLen,\r
+                      OpCode\r
+                      );\r
+\r
+  if (OptionBufferPtr != NULL) {\r
+    return OptionBufferPtr;\r
+  }\r
+\r
+  if (Overload & OVLD_FILE) {\r
+    OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpBootFile, 128, OpCode);\r
+\r
+    if (OptionBufferPtr != NULL) {\r
+      return OptionBufferPtr;\r
+    }\r
+  }\r
+\r
+  if (Overload & OVLD_SRVR_NAME) {\r
+    OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpSrvName, 64, OpCode);\r
+\r
+    if (OptionBufferPtr != NULL) {\r
+      return OptionBufferPtr;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Download file into buffer\r
+\r
+  @param  Private                                     Pointer to PxeBc interface\r
+  @param  BufferSize                                  pointer to size of download\r
+                                                      buffer\r
+  @param  Buffer                                      Pointer to buffer\r
+\r
+  @return EFI_BUFFER_TOO_SMALL -\r
+  @return EFI_NOT_FOUND -\r
+  @return EFI_PROTOCOL_ERROR -\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DownloadFile (\r
+  IN PXE_BASECODE_DEVICE  *Private,\r
+  IN OUT UINT64           *BufferSize,\r
+  IN VOID                 *Buffer\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MTFTP_INFO  MtftpInfo;\r
+  EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode;\r
+  DHCP_RECEIVE_BUFFER           *RxBuf;\r
+  EFI_STATUS                    Status;\r
+  UINTN                         BlockSize;\r
+\r
+  RxBuf     = (DHCP_RECEIVE_BUFFER *) Private->BootServerReceiveBuffer;\r
+  BlockSize = 0x8000;\r
+\r
+  DEBUG ((EFI_D_WARN, "\nDownloadFile()  Enter."));\r
+\r
+  if (Buffer == NULL || *BufferSize == 0 || *BufferSize < Private->FileSize) {\r
+    if (Private->FileSize != 0) {\r
+      *BufferSize = Private->FileSize;\r
+      return EFI_BUFFER_TOO_SMALL;\r
+    }\r
+\r
+    AsciiPrint ("\nTSize");\r
+\r
+    OpCode = EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE;\r
+  } else if (RxBuf->OpAdds.Status & WfM11a_TYPE) {\r
+    OpCode = EFI_PXE_BASE_CODE_MTFTP_READ_FILE;\r
+\r
+    ZeroMem (&MtftpInfo, sizeof MtftpInfo);\r
+\r
+    *(IPV4_ADDR *) &MtftpInfo.MCastIp = *(IPV4_ADDR *) RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_IP - 1]->Data;\r
+\r
+    CopyMem (\r
+      &MtftpInfo.CPort,\r
+      RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_CPORT - 1]->Data,\r
+      sizeof MtftpInfo.CPort\r
+      );\r
+\r
+    CopyMem (\r
+      &MtftpInfo.SPort,\r
+      RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_SPORT - 1]->Data,\r
+      sizeof MtftpInfo.SPort\r
+      );\r
+\r
+    MtftpInfo.ListenTimeout   = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_TMOUT - 1]);\r
+\r
+    MtftpInfo.TransmitTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_DELAY - 1]);\r
+\r
+    AsciiPrint ("\nMTFTP");\r
+  } else {\r
+    AsciiPrint ("\nTFTP");\r
+\r
+    OpCode = EFI_PXE_BASE_CODE_TFTP_READ_FILE;\r
+  }\r
+\r
+  Private->FileSize = 0;\r
+\r
+  RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data[RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length] = 0;\r
+\r
+  Status = Private->EfiBc.Mtftp (\r
+                            &Private->EfiBc,\r
+                            OpCode,\r
+                            Buffer,\r
+                            FALSE,\r
+                            BufferSize,\r
+                            &BlockSize,\r
+                            &Private->ServerIp,\r
+                            (UINT8 *) RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data,\r
+                            &MtftpInfo,\r
+                            FALSE\r
+                            );\r
+\r
+  if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {\r
+    DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #1 %Xh", Status));\r
+    return Status;\r
+  }\r
+\r
+  if (sizeof (UINTN) < sizeof (UINT64) && *BufferSize > 0xFFFFFFFF) {\r
+    Private->FileSize = 0xFFFFFFFF;\r
+  } else {\r
+    Private->FileSize = (UINTN) *BufferSize;\r
+  }\r
+\r
+  if (OpCode == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {\r
+    DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #2"));\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #3 %Xh", Status));\r
+    return Status;\r
+  }\r
+\r
+  if (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected && Private->EfiBc.Mode->PxeBisReplyReceived) {\r
+    UINT64  CredentialLen;\r
+    UINTN   BlockSize;\r
+    UINT8   CredentialFilename[256];\r
+    UINT8   *op;\r
+    VOID    *CredentialBuffer;\r
+\r
+    //\r
+    // Get name of credential file.  It may be in the BOOTP\r
+    // bootfile field or a DHCP option.\r
+    //\r
+    ZeroMem (CredentialFilename, sizeof CredentialFilename);\r
+\r
+    op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_DHCP_BOOTFILE);\r
+\r
+    if (op != NULL) {\r
+      if (op[1] == 0) {\r
+        /* No credential filename */\r
+        return EFI_NOT_FOUND;\r
+      }\r
+\r
+      CopyMem (CredentialFilename, &op[2], op[1]);\r
+    } else {\r
+      if (Private->EfiBc.Mode->PxeBisReply.Dhcpv4.BootpBootFile[0] == 0) {\r
+        /* No credential filename */\r
+        return EFI_NOT_FOUND;\r
+      }\r
+\r
+      CopyMem (CredentialFilename, &op[2], 128);\r
+    }\r
+    //\r
+    // Get size of credential file.  It may be available as a\r
+    // DHCP option.  If not, use the TFTP get file size.\r
+    //\r
+    CredentialLen = 0;\r
+\r
+    op            = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_BOOT_FILE_SZ);\r
+\r
+    if (op != NULL) {\r
+      /*\r
+       * This is actually the size of the credential file\r
+       * buffer.  The actual credential file size will be\r
+       * returned when we download the file.\r
+       */\r
+      if (op[1] == 2) {\r
+        UINT16  n;\r
+\r
+        CopyMem (&n, &op[2], 2);\r
+        CredentialLen = HTONS (n) * 512;\r
+      }\r
+    }\r
+\r
+    if (CredentialLen == 0) {\r
+      BlockSize = 8192;\r
+\r
+      Status = Private->EfiBc.Mtftp (\r
+                                &Private->EfiBc,\r
+                                EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
+                                NULL,\r
+                                FALSE,\r
+                                &CredentialLen,\r
+                                &BlockSize,\r
+                                &Private->ServerIp,\r
+                                CredentialFilename,\r
+                                NULL,\r
+                                FALSE\r
+                                );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+\r
+      if (CredentialLen == 0) {\r
+        //\r
+        // %%TBD -- EFI error for invalid credential\r
+        // file.\r
+        //\r
+        return EFI_PROTOCOL_ERROR;\r
+      }\r
+    }\r
+    //\r
+    // Allocate credential file buffer.\r
+    //\r
+    Status = gBS->AllocatePool (\r
+                    EfiBootServicesData,\r
+                    (UINTN) CredentialLen,\r
+                    &CredentialBuffer\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    //\r
+    // Download credential file.\r
+    //\r
+    BlockSize = 8192;\r
+\r
+    Status = Private->EfiBc.Mtftp (\r
+                              &Private->EfiBc,\r
+                              EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
+                              CredentialBuffer,\r
+                              FALSE,\r
+                              &CredentialLen,\r
+                              &BlockSize,\r
+                              &Private->ServerIp,\r
+                              CredentialFilename,\r
+                              NULL,\r
+                              FALSE\r
+                              );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->FreePool (CredentialBuffer);\r
+      return Status;\r
+    }\r
+    //\r
+    // Verify credentials.\r
+    //\r
+    if (PxebcBisVerify (Private, Buffer, Private->FileSize, CredentialBuffer, (UINTN) CredentialLen)) {\r
+      Status = EFI_SUCCESS;\r
+    } else {\r
+      //\r
+      // %%TBD -- An EFI error code for failing credential verification.\r
+      //\r
+      Status = EFI_PROTOCOL_ERROR;\r
+    }\r
+\r
+    gBS->FreePool (CredentialBuffer);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start PXE DHCP.  Get DHCP and proxyDHCP information.\r
+  Display remote boot menu and prompt.  Select item from menu.\r
+\r
+  @param  Private                                     Pointer to PxeBc interface\r
+  @param  BufferSize                                  Pointer to download buffer\r
+                                                      size\r
+  @param  Buffer                                      Pointer to download buffer\r
+\r
+  @retval EFI_SUCCESS\r
+  @retval EFI_NOT_READY\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+LoadfileStart (\r
+  IN PXE_BASECODE_DEVICE  *Private,\r
+  IN OUT UINT64           *BufferSize,\r
+  IN VOID                 *Buffer\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE      *PxeBcMode;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+  EFI_SIMPLE_NETWORK_MODE     *SnpMode;\r
+  EFI_STATUS                  Status;\r
+  VOID                        *RxBuf;\r
+\r
+  DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Enter."));\r
+\r
+  //\r
+  // Try to start BaseCode, for now only IPv4 is supported\r
+  // so don't try to start using IPv6.\r
+  //\r
+  Status = Private->EfiBc.Start (&Private->EfiBc, FALSE);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status != EFI_ALREADY_STARTED) {\r
+      DEBUG ((DEBUG_NET, "\nLoadfileStart()  Exit  BC.Start() == %xh", Status));\r
+      return Status;\r
+    }\r
+  }\r
+  //\r
+  // Get pointers to PXE mode structure, SNP protocol structure\r
+  // and SNP mode structure.\r
+  //\r
+  PxeBcMode = Private->EfiBc.Mode;\r
+  Snp       = Private->SimpleNetwork;\r
+  SnpMode   = Snp->Mode;\r
+\r
+  //\r
+  // Display client MAC address, like 16-bit PXE ROMs\r
+  //\r
+  AsciiPrint ("\nCLIENT MAC ADDR: ");\r
+\r
+  {\r
+    UINTN Index;\r
+    UINTN hlen;\r
+\r
+    hlen = SnpMode->HwAddressSize;\r
+\r
+    for (Index = 0; Index < hlen; ++Index) {\r
+      AsciiPrint ("%02x ", SnpMode->CurrentAddress.Addr[Index]);\r
+    }\r
+  }\r
+\r
+  AsciiPrint ("\nDHCP");\r
+\r
+  Status = Private->EfiBc.Dhcp (&Private->EfiBc, TRUE);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  BC.Dhcp() == %Xh", Status));\r
+    AsciiPrint ("\r               \r");\r
+    return Status;\r
+  }\r
+\r
+  ShowMyInfo (Private);\r
+\r
+  RxBuf = PxeBcMode->ProxyOfferReceived ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER;\r
+#define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)\r
+\r
+  Status = DoMenu (Private, RxBufferPtr);\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    //\r
+    // did a discovery - take info from discovery packet\r
+    //\r
+    RxBuf = &PXE_ACK_BUFFER;\r
+  } else if (Status == NO_MENU) {\r
+    //\r
+    // did not do a discovery - take info from rxbuf\r
+    //\r
+    Private->ServerIp.Addr[0] = RxBufferPtr->u.Dhcpv4.siaddr;\r
+\r
+    if (!(Private->ServerIp.Addr[0])) {\r
+      *(IPV4_ADDR *) &Private->ServerIp = *(IPV4_ADDR *) RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]->Data;\r
+    }\r
+  } else {\r
+    DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  DoMenu() == %Xh", Status));\r
+    return Status;\r
+  }\r
+\r
+  if (!RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {\r
+    DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  Not ready?"));\r
+    return EFI_NOT_READY;\r
+  }\r
+  //\r
+  // check for file size option sent\r
+  //\r
+  if (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]) {\r
+    Private->FileSize = 512 * NTOHS (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]->Data);\r
+  }\r
+\r
+  Private->BootServerReceiveBuffer  = RxBufferPtr;\r
+\r
+  Status = DownloadFile (Private, BufferSize, Buffer);\r
+\r
+  DEBUG (\r
+    (DEBUG_WARN,\r
+    "\nLoadfileStart()  Exit.  DownloadFile() = %Xh",\r
+    Status)\r
+    );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Loadfile interface for PxeBc interface\r
+\r
+  @param  This                                        Pointer to Loadfile interface\r
+  @param  FilePath                                    Not used and not checked\r
+  @param  BootPolicy                                  Must be TRUE\r
+  @param  BufferSize                                  Pointer to buffer size\r
+  @param  Buffer                                      Pointer to download buffer or\r
+                                                      NULL\r
+\r
+  @return EFI_INVALID_PARAMETER -\r
+  @return EFI_UNSUPPORTED -\r
+  @return EFI_SUCCESS -\r
+  @return EFI_BUFFER_TOO_SMALL -\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LoadFile (\r
+  IN EFI_LOAD_FILE_PROTOCOL           *This,\r
+  IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,\r
+  IN BOOLEAN                          BootPolicy,\r
+  IN OUT UINTN                        *BufferSize,\r
+  IN OUT VOID                         *Buffer\r
+  )\r
+{\r
+  LOADFILE_DEVICE *LoadfilePtr;\r
+  UINT64          TmpBufSz;\r
+  INT32           OrigMode;\r
+  INT32           OrigAttribute;\r
+  BOOLEAN         RemoveCallback;\r
+  BOOLEAN         NewMakeCallback;\r
+  EFI_STATUS      Status;\r
+  EFI_STATUS      TempStatus;\r
+  //\r
+  //\r
+  //\r
+  OrigMode        = gST->ConOut->Mode->Mode;\r
+  OrigAttribute   = gST->ConOut->Mode->Attribute;\r
+  RemoveCallback  = FALSE;\r
+\r
+  AsciiPrint ("Running LoadFile()\n");\r
+\r
+  //\r
+  // Resolve Warning 4 unreferenced parameter problem\r
+  //\r
+  FilePath = NULL;\r
+\r
+  //\r
+  // If either if these parameters are NULL, we cannot continue.\r
+  //\r
+  if (This == NULL || BufferSize == NULL) {\r
+    DEBUG ((DEBUG_WARN, "\nLoadFile()  This or BufferSize == NULL"));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // We only support BootPolicy == TRUE\r
+  //\r
+  if (!BootPolicy) {\r
+    DEBUG ((DEBUG_WARN, "\nLoadFile()  BootPolicy == FALSE"));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  //\r
+  // Get pointer to LoadFile protocol structure.\r
+  //\r
+  LoadfilePtr = CR (This, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE);\r
+\r
+  if (LoadfilePtr == NULL) {\r
+    DEBUG (\r
+      (DEBUG_NET,\r
+      "\nLoadFile()  Could not get pointer to LoadFile structure")\r
+      );\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Lock interface\r
+  //\r
+  EfiAcquireLock (&LoadfilePtr->Lock);\r
+\r
+  //\r
+  // Set console output mode and display attribute\r
+  //\r
+  if (OrigMode != 0) {\r
+    gST->ConOut->SetMode (gST->ConOut, 0);\r
+  }\r
+\r
+  gST->ConOut->SetAttribute (\r
+                gST->ConOut,\r
+                EFI_TEXT_ATTR (EFI_LIGHTGRAY,EFI_BLACK)\r
+                );\r
+\r
+  //\r
+  // See if BaseCode already has a Callback protocol attached.\r
+  // If there is none, attach our own Callback protocol.\r
+  //\r
+  Status = gBS->HandleProtocol (\r
+                  LoadfilePtr->Private->Handle,\r
+                  &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+                  (VOID *) &LoadfilePtr->Private->CallbackProtocolPtr\r
+                  );\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    //\r
+    // There is already a callback routine.  Do nothing.\r
+    //\r
+    DEBUG ((DEBUG_WARN, "\nLoadFile()  BC callback exists."));\r
+\r
+  } else if (Status == EFI_UNSUPPORTED) {\r
+    //\r
+    // No BaseCode Callback protocol found.  Add our own.\r
+    //\r
+    Status = gBS->InstallProtocolInterface (\r
+                    &LoadfilePtr->Private->Handle,\r
+                    &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+                    EFI_NATIVE_INTERFACE,\r
+                    &_bc_callback\r
+                    );\r
+\r
+    DEBUG ((DEBUG_WARN, "\nLoadFile()  Callback install status == %xh", Status));\r
+\r
+    RemoveCallback = (BOOLEAN) (Status == EFI_SUCCESS);\r
+\r
+    if (LoadfilePtr->Private->EfiBc.Mode != NULL && LoadfilePtr->Private->EfiBc.Mode->Started) {\r
+      NewMakeCallback = TRUE;\r
+      LoadfilePtr->Private->EfiBc.SetParameters (\r
+                                    &LoadfilePtr->Private->EfiBc,\r
+                                    NULL,\r
+                                    NULL,\r
+                                    NULL,\r
+                                    NULL,\r
+                                    &NewMakeCallback\r
+                                    );\r
+    }\r
+\r
+  } else {\r
+    DEBUG ((DEBUG_WARN, "\nLoadFile()  Callback check status == %xh", Status));\r
+  }\r
+  //\r
+  // Check for starting or for continuing after already getting\r
+  // the file size.\r
+  //\r
+  if (LoadfilePtr->Private->FileSize == 0) {\r
+    TmpBufSz  = 0;\r
+    Status    = LoadfileStart (LoadfilePtr->Private, &TmpBufSz, Buffer);\r
+\r
+    if (sizeof (UINTN) < sizeof (UINT64) && TmpBufSz > 0xFFFFFFFF) {\r
+      *BufferSize = 0xFFFFFFFF;\r
+    } else {\r
+      *BufferSize = (UINTN) TmpBufSz;\r
+    }\r
+\r
+    if (Status == EFI_BUFFER_TOO_SMALL) {\r
+      //\r
+      // This is done so loadfile will work even if the boot manager\r
+      // did not make the first call with Buffer == NULL.\r
+      //\r
+      Buffer = NULL;\r
+    }\r
+  } else if (Buffer == NULL) {\r
+    DEBUG ((DEBUG_WARN, "\nLoadfile()  Get buffer size"));\r
+\r
+    //\r
+    // Continuing from previous LoadFile request.  Make sure there\r
+    // is a buffer and that it is big enough.\r
+    //\r
+    *BufferSize = LoadfilePtr->Private->FileSize;\r
+    Status      = EFI_BUFFER_TOO_SMALL;\r
+  } else {\r
+    DEBUG ((DEBUG_WARN, "\nLoadFile()  Download file"));\r
+\r
+    //\r
+    // Everything looks good, try to download the file.\r
+    //\r
+    TmpBufSz  = *BufferSize;\r
+    Status    = DownloadFile (LoadfilePtr->Private, &TmpBufSz, Buffer);\r
+\r
+    //\r
+    // Next call to loadfile will start DHCP process again.\r
+    //\r
+    LoadfilePtr->Private->FileSize = 0;\r
+  }\r
+  //\r
+  // If we added a callback protocol, now is the time to remove it.\r
+  //\r
+  if (RemoveCallback) {\r
+    NewMakeCallback = FALSE;\r
+    TempStatus = LoadfilePtr->Private->EfiBc.SetParameters (\r
+                                          &LoadfilePtr->Private->EfiBc,\r
+                                          NULL,\r
+                                          NULL,\r
+                                          NULL,\r
+                                          NULL,\r
+                                          &NewMakeCallback\r
+                                          );\r
+\r
+    if (TempStatus == EFI_SUCCESS) {\r
+      gBS->UninstallProtocolInterface (\r
+            LoadfilePtr->Private->Handle,\r
+            &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+            &_bc_callback\r
+            );\r
+    }\r
+  }\r
+  //\r
+  // Restore display mode and attribute\r
+  //\r
+  if (OrigMode != 0) {\r
+    gST->ConOut->SetMode (gST->ConOut, OrigMode);\r
+  }\r
+\r
+  gST->ConOut->SetAttribute (gST->ConOut, OrigAttribute);\r
+\r
+  //\r
+  // Unlock interface\r
+  //\r
+  EfiReleaseLock (&LoadfilePtr->Lock);\r
+\r
+  DEBUG ((DEBUG_WARN, "\nBC.Loadfile()  Status == %xh\n", Status));\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    return EFI_SUCCESS;\r
+\r
+  } else if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    //\r
+    // Error is only displayed when we are actually trying to\r
+    // download the boot image.\r
+    //\r
+    if (Buffer == NULL) {\r
+      return EFI_BUFFER_TOO_SMALL;\r
+    }\r
+\r
+    AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");\r
+\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    AsciiPrint ("\nPXE-E07: Network device error.  Check network connection.\n");\r
+\r
+  } else if (Status == EFI_OUT_OF_RESOURCES) {\r
+    AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");\r
+\r
+  } else if (Status == EFI_NO_MEDIA) {\r
+    AsciiPrint ("\nPXE-E12: Could not detect network connection.  Check cable.\n");\r
+\r
+  } else if (Status == EFI_NO_RESPONSE) {\r
+    AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");\r
+\r
+  } else if (Status == EFI_TIMEOUT) {\r
+    AsciiPrint ("\nPXE-E18: Timeout.  Server did not respond.\n");\r
+\r
+  } else if (Status == EFI_ABORTED) {\r
+    AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");\r
+\r
+  } else if (Status == EFI_ICMP_ERROR) {\r
+    AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");\r
+\r
+    if (LoadfilePtr->Private->EfiBc.Mode != NULL) {\r
+      if (LoadfilePtr->Private->EfiBc.Mode->IcmpErrorReceived) {\r
+\r
+      AsciiPrint (\r
+          "PXE-E98: Type: %xh  Code: %xh  ",\r
+          LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type,\r
+          LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code\r
+          );\r
+\r
+        switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type) {\r
+        case 0x03:\r
+          switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code) {\r
+          case 0x00:              /* net unreachable */\r
+          AsciiPrint ("Net unreachable");\r
+            break;\r
+\r
+          case 0x01:              /* host unreachable */\r
+          AsciiPrint ("Host unreachable");\r
+            break;\r
+\r
+          case 0x02:              /* protocol unreachable */\r
+          AsciiPrint ("Protocol unreachable");\r
+            break;\r
+\r
+          case 0x03:              /* port unreachable */\r
+          AsciiPrint ("Port unreachable");\r
+            break;\r
+\r
+          case 0x04:              /* Fragmentation needed */\r
+          AsciiPrint ("Fragmentation needed");\r
+            break;\r
+\r
+          case 0x05:              /* Source route failed */\r
+          AsciiPrint ("Source route failed");\r
+            break;\r
+          }\r
+\r
+          break;\r
+        }\r
+\r
+      AsciiPrint ("\n");\r
+      }\r
+    }\r
+\r
+  } else if (Status == EFI_TFTP_ERROR) {\r
+    AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");\r
+\r
+    if (LoadfilePtr->Private->EfiBc.Mode != NULL) {\r
+      if (LoadfilePtr->Private->EfiBc.Mode->TftpErrorReceived) {\r
+      AsciiPrint (\r
+          "PXE-E98: Code: %xh  %a\n",\r
+          LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorCode,\r
+          LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorString\r
+          );\r
+      }\r
+    }\r
+\r
+  } else {\r
+    AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status);\r
+  }\r
+\r
+  LoadfilePtr->Private->EfiBc.Stop (&LoadfilePtr->Private->EfiBc);\r
+\r
+  return Status;\r
+}\r
diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h
new file mode 100644 (file)
index 0000000..f81d86c
--- /dev/null
@@ -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 (file)
index 0000000..a8fa293
--- /dev/null
@@ -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 (file)
index 0000000..7186a9f
--- /dev/null
@@ -0,0 +1,169 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  ComponentName.c\r
+\r
+Abstract:\r
+  PxeDhcp4 component name protocol declarations\r
+\r
+\r
+**/\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  );\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+EFI_COMPONENT_NAME_PROTOCOL    gPxeDhcp4ComponentName = {\r
+  PxeDhcp4ComponentNameGetDriverName,\r
+  PxeDhcp4ComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+static EFI_UNICODE_STRING_TABLE mPxeDhcp4DriverNameTable[] = {\r
+  {\r
+    "eng",\r
+    L"PXE DHCPv4 Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the EFI Driver.\r
+\r
+  Arguments:\r
+    This       - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    Language   - A pointer to a three character ISO 639-2 language identifier.\r
+                 This is the language of the driver name that that the caller\r
+                 is requesting, and it must match one of the languages specified\r
+                 in SupportedLanguages.  The number of languages supported by a\r
+                 driver is up to the driver writer.\r
+    DriverName - A pointer to the Unicode string to return.  This Unicode string\r
+                 is the name of the driver specified by This in the language\r
+                 specified by Language.\r
+\r
+  Returns:\r
+    EFI_SUCCESS           - The Unicode string for the Driver specified by This\r
+                            and the language specified by Language was returned\r
+                            in DriverName.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - DriverName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return LookupUnicodeString (\r
+          Language,\r
+          gPxeDhcp4ComponentName.SupportedLanguages,\r
+          mPxeDhcp4DriverNameTable,\r
+          DriverName\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  )\r
+/*++\r
+\r
+  Routine Description:\r
+    Retrieves a Unicode string that is the user readable name of the controller\r
+    that is being managed by an EFI Driver.\r
+\r
+  Arguments:\r
+    This             - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.\r
+    ControllerHandle - The handle of a controller that the driver specified by\r
+                       This is managing.  This handle specifies the controller\r
+                       whose name is to be returned.\r
+    ChildHandle      - The handle of the child controller to retrieve the name\r
+                       of.  This is an optional parameter that may be NULL.  It\r
+                       will be NULL for device drivers.  It will also be NULL\r
+                       for a bus drivers that wish to retrieve the name of the\r
+                       bus controller.  It will not be NULL for a bus driver\r
+                       that wishes to retrieve the name of a child controller.\r
+    Language         - A pointer to a three character ISO 639-2 language\r
+                       identifier.  This is the language of the controller name\r
+                       that that the caller is requesting, and it must match one\r
+                       of the languages specified in SupportedLanguages.  The\r
+                       number of languages supported by a driver is up to the\r
+                       driver writer.\r
+    ControllerName   - A pointer to the Unicode string to return.  This Unicode\r
+                       string is the name of the controller specified by\r
+                       ControllerHandle and ChildHandle in the language specified\r
+                       by Language from the point of view of the driver specified\r
+                       by This.\r
+\r
+  Returns:\r
+    EFI_SUCCESS           - The Unicode string for the user readable name in the\r
+                            language specified by Language for the driver\r
+                            specified by This was returned in DriverName.\r
+    EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.\r
+    EFI_INVALID_PARAMETER - Language is NULL.\r
+    EFI_INVALID_PARAMETER - ControllerName is NULL.\r
+    EFI_UNSUPPORTED       - The driver specified by This is not currently managing\r
+                            the controller specified by ControllerHandle and\r
+                            ChildHandle.\r
+    EFI_UNSUPPORTED       - The driver specified by This does not support the\r
+                            language specified by Language.\r
+\r
+--*/\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/* EOF - ComponentName.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c
new file mode 100644 (file)
index 0000000..fe4da51
--- /dev/null
@@ -0,0 +1,355 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2005, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeDhcp4.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// Prototypes\r
+// Driver model protocol interface\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4DriverEntryPoint (\r
+  IN EFI_HANDLE           ImageHandle,\r
+  IN EFI_SYSTEM_TABLE     *SystemTable\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN  EFI_HANDLE                     ControllerHandle,\r
+  IN  UINTN                          NumberOfChildren,\r
+  IN  EFI_HANDLE                     *ChildHandleBuffer\r
+  );\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// PXE DHCP Protocol Interface\r
+//\r
+EFI_DRIVER_BINDING_PROTOCOL gPxeDhcp4DriverBinding = {\r
+  PxeDhcp4DriverBindingSupported,\r
+  PxeDhcp4DriverBindingStart,\r
+  PxeDhcp4DriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+//\r
+// PxeDhcp4 Driver Entry point funtion\r
+//\r
+\r
+/**\r
+  Register Driver Binding protocol for this driver.\r
+\r
+  @param  entry                EFI_IMAGE_ENTRY_POINT)\r
+\r
+  @retval EFI_SUCCESS          Driver loaded.\r
+  @retval other                Driver not loaded.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4DriverEntryPoint (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  return EfiLibInstallAllDriverProtocols (\r
+          ImageHandle,\r
+          SystemTable,\r
+          &gPxeDhcp4DriverBinding,\r
+          NULL,\r
+          COMPONENT_NAME,\r
+          NULL,\r
+          NULL\r
+          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle.  Any\r
+  ControllerHandle that contains a PxeBaseCode protocol can be\r
+  supported.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  ControllerHandle     Handle of device to test.\r
+  @param  RemainingDevicePath  Not used.\r
+\r
+  @retval EFI_SUCCESS          This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on this device.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  * This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     * RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;\r
+\r
+  //\r
+  // Open the IO Abstraction(s) needed to perform the supported test.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiPxeBaseCodeProtocolGuid,\r
+                  (VOID **) &PxeBc,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Close the I/O Abstraction(s) used to perform the supported test.\r
+  //\r
+  return gBS->CloseProtocol (\r
+                ControllerHandle,\r
+                &gEfiPxeBaseCodeProtocolGuid,\r
+                This->DriverBindingHandle,\r
+                ControllerHandle\r
+                );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+  Start this driver on ControllerHandle by opening a PxeBaseCode\r
+  protocol and installing a PxeDhcp4 protocol on ControllerHandle.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  ControllerHandle     Handle of device to bind driver to.\r
+  @param  RemainingDevicePath  Not used, always produce all possible children.\r
+\r
+  @retval EFI_SUCCESS          This driver is added to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  * This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     * RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+  PXE_DHCP4_PRIVATE_DATA      *Private;\r
+\r
+  //\r
+  // Connect to the PxeBaseCode interface on ControllerHandle.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiPxeBaseCodeProtocolGuid,\r
+                  (VOID **) &PxeBc,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // BaseCode has already grabbed the SimpleNetwork interface\r
+  // so just do a HandleProtocol() to get it.\r
+  //\r
+  Status = gBS->HandleProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiSimpleNetworkProtocolGuid,\r
+                  (VOID **) &Snp\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto error_exit;\r
+  }\r
+\r
+  ASSERT (Snp);\r
+\r
+  //\r
+  // Initialize the PXE DHCP device instance.\r
+  //\r
+  Private = AllocateZeroPool (sizeof (PXE_DHCP4_PRIVATE_DATA));\r
+  if (Private == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto error_exit;\r
+  }\r
+\r
+  Private->Signature          = PXE_DHCP4_PRIVATE_DATA_SIGNATURE;\r
+  Private->PxeBc              = PxeBc;\r
+  Private->Snp                = Snp;\r
+  Private->Handle             = ControllerHandle;\r
+  Private->PxeDhcp4.Revision  = EFI_PXE_DHCP4_PROTOCOL_REVISION;\r
+  Private->PxeDhcp4.Run       = PxeDhcp4Run;\r
+  Private->PxeDhcp4.Setup     = PxeDhcp4Setup;\r
+  Private->PxeDhcp4.Init      = PxeDhcp4Init;\r
+  Private->PxeDhcp4.Select    = PxeDhcp4Select;\r
+  Private->PxeDhcp4.Renew     = PxeDhcp4Renew;\r
+  Private->PxeDhcp4.Rebind    = PxeDhcp4Rebind;\r
+  Private->PxeDhcp4.Release   = PxeDhcp4Release;\r
+  Private->PxeDhcp4.Data      = NULL;\r
+\r
+  //\r
+  // Install protocol interfaces for the PXE DHCP device.\r
+  //\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &ControllerHandle,\r
+                  &gEfiPxeDhcp4ProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &Private->PxeDhcp4\r
+                  );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+error_exit: ;\r
+  gBS->CloseProtocol (\r
+        ControllerHandle,\r
+        &gEfiPxeBaseCodeProtocolGuid,\r
+        This->DriverBindingHandle,\r
+        ControllerHandle\r
+        );\r
+\r
+  return Status;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+  Stop this driver on ControllerHandle by removing PXE DHCP\r
+  protocol and closing the PXE Base Code protocol on\r
+  ControllerHandle.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  ControllerHandle     Handle of device to stop driver on.\r
+  @param  NumberOfChildren     Not used.\r
+  @param  ChildHandleBuffer    Not used.\r
+\r
+  @retval EFI_SUCCESS          This driver is removed ControllerHandle.\r
+  @retval other                This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN  EFI_HANDLE                     ControllerHandle,\r
+  IN  UINTN                          NumberOfChildren,\r
+  IN  EFI_HANDLE                     *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  EFI_PXE_DHCP4_PROTOCOL  *PxeDhcp4;\r
+  PXE_DHCP4_PRIVATE_DATA  *Private;\r
+\r
+  //\r
+  // Get our context back.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiPxeDhcp4ProtocolGuid,\r
+                  (VOID **) &PxeDhcp4,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (PxeDhcp4);\r
+\r
+  //\r
+  // Release allocated resources\r
+  //\r
+  if (Private->PxeDhcp4.Data) {\r
+    FreePool (Private->PxeDhcp4.Data);\r
+    Private->PxeDhcp4.Data = NULL;\r
+  }\r
+  //\r
+  // Uninstall our protocol\r
+  //\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  ControllerHandle,\r
+                  &gEfiPxeDhcp4ProtocolGuid,\r
+                  &Private->PxeDhcp4\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Close any consumed protocols\r
+  //\r
+  Status = gBS->CloseProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiPxeBaseCodeProtocolGuid,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Release our private data\r
+  //\r
+  FreePool (Private);\r
+\r
+  return Status;\r
+}\r
+\r
+/* EOF - PxeDhcp4.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h
new file mode 100644 (file)
index 0000000..b33ed3e
--- /dev/null
@@ -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
+
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/PxeBaseCode.h>\r
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/PxeDhcp4.h>
+#include <Protocol/PxeDhcp4Callback.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#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 (file)
index 0000000..70daa05
--- /dev/null
@@ -0,0 +1,64 @@
+#/** @file\r
+# Component name for module PxeDhcp4\r
+#\r
+# Copyright (c) 2007, Intel Corporation\r
+#\r
+#  All rights reserved. This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution. The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = PxeDhcp4Dxe\r
+  FILE_GUID                      = a46c3330-be36-4977-9d24-a7cf92eef0fe\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  EDK_RELEASE_VERSION            = 0x00020000\r
+  EFI_SPECIFICATION_VERSION      = 0x00020000\r
+\r
+  ENTRY_POINT                    = PxeDhcp4DriverEntryPoint\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources.common]\r
+  support.c\r
+  PxeDhcp4Release.c\r
+  PxeDhcp4Setup.c\r
+  ComponentName.c\r
+  PxeDhcp4RenewRebind.c\r
+  PxeDhcp4.h\r
+  PxeDhcp4.c\r
+  PxeDhcp4InitSelect.c\r
+  PxeDhcp4Run.c\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  UefiLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  BaseMemoryLib\r
+  DebugLib\r
+\r
+\r
+[Protocols]\r
+  gEfiPxeBaseCodeProtocolGuid                   # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiSimpleNetworkProtocolGuid                 # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiPxeDhcp4CallbackProtocolGuid              # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiPxeDhcp4ProtocolGuid                      # PROTOCOL ALWAYS_CONSUMED\r
+\r
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa
new file mode 100644 (file)
index 0000000..5db7cf4
--- /dev/null
@@ -0,0 +1,78 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+  <MsaHeader>\r
+    <ModuleName>PxeDhcp4</ModuleName>\r
+    <ModuleType>DXE_DRIVER</ModuleType>\r
+    <GuidValue>a46c3330-be36-4977-9d24-a7cf92eef0fe</GuidValue>\r
+    <Version>1.0</Version>\r
+    <Abstract>Component name for module PxeDhcp4</Abstract>\r
+    <Description>FIX ME!</Description>\r
+    <Copyright>Copyright (c) 2007, Intel Corporation. All rights reserved.</Copyright>\r
+    <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>\r
+    <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION   0x00000052</Specification>\r
+  </MsaHeader>\r
+  <ModuleDefinitions>\r
+    <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>\r
+    <BinaryModule>false</BinaryModule>\r
+    <OutputFileBasename>PxeDhcp4</OutputFileBasename>\r
+  </ModuleDefinitions>\r
+  <LibraryClassDefinitions>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>DebugLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>BaseMemoryLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiDriverEntryPoint</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiBootServicesTableLib</Keyword>\r
+    </LibraryClass>\r
+    <LibraryClass Usage="ALWAYS_CONSUMED">\r
+      <Keyword>UefiLib</Keyword>\r
+    </LibraryClass>\r
+  </LibraryClassDefinitions>\r
+  <SourceFiles>\r
+    <Filename>PxeDhcp4Run.c</Filename>\r
+    <Filename>PxeDhcp4InitSelect.c</Filename>\r
+    <Filename>PxeDhcp4Entry.c</Filename>\r
+    <Filename>PxeDhcp4.c</Filename>\r
+    <Filename>PxeDhcp4.h</Filename>\r
+    <Filename>PxeDhcp4RenewRebind.c</Filename>\r
+    <Filename>ComponentName.c</Filename>\r
+    <Filename>PxeDhcp4Setup.c</Filename>\r
+    <Filename>PxeDhcp4Release.c</Filename>\r
+    <Filename>support.c</Filename>\r
+  </SourceFiles>\r
+  <PackageDependencies>\r
+    <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>\r
+    <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>\r
+  </PackageDependencies>\r
+  <Protocols>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiPxeDhcp4ProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiPxeDhcp4CallbackProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiSimpleNetworkProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+    <Protocol Usage="ALWAYS_CONSUMED">\r
+      <ProtocolCName>gEfiPxeBaseCodeProtocolGuid</ProtocolCName>\r
+    </Protocol>\r
+  </Protocols>\r
+  <Externs>\r
+    <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>\r
+    <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>\r
+    <Extern>\r
+      <ModuleEntryPoint>PxeDhcp4DriverEntryPoint</ModuleEntryPoint>\r
+    </Extern>\r
+  </Externs>\r
+</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 (file)
index 0000000..b7849d0
--- /dev/null
@@ -0,0 +1,784 @@
+/** @file\r
+\r
+Copyright (c) 2004, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeDhcp4InitSelect.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+#define DebugPrint(x)\r
+//\r
+// #define DebugPrint(x) Aprint x\r
+//\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+**/\r
+STATIC\r
+INTN\r
+offer_verify (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN DHCP4_PACKET           *tx_pkt,\r
+  IN DHCP4_PACKET           *rx_pkt,\r
+  IN UINTN                  rx_pkt_size\r
+  )\r
+{\r
+  EFI_STATUS    EfiStatus;\r
+  DHCP4_PACKET  *tmp;\r
+  DHCP4_OP      *msg_type_op;\r
+  DHCP4_OP      *srvid_op;\r
+  UINT32        magik;\r
+\r
+  //\r
+  // Verify parameters.  Touch unused parameters to keep\r
+  // compiler happy.\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (rx_pkt);\r
+\r
+  if (Private == NULL || rx_pkt == NULL) {\r
+    return -2;\r
+  }\r
+\r
+  tx_pkt      = tx_pkt;\r
+  rx_pkt_size = rx_pkt_size;\r
+\r
+  //\r
+  // This may be a BOOTP Reply or DHCP Offer packet.\r
+  // If there is no DHCP magik number, assume that\r
+  // this is a BOOTP Reply packet.\r
+  //\r
+  magik = htonl (DHCP4_MAGIK_NUMBER);\r
+\r
+  while (!CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {\r
+    //\r
+    // If there is no DHCP message type option, assume\r
+    // this is a BOOTP reply packet and cache it.\r
+    //\r
+    EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);\r
+\r
+    if (EFI_ERROR (EfiStatus)) {\r
+      break;\r
+    }\r
+    //\r
+    // If there is a DHCP message type option, it must be a\r
+    // DHCP offer packet\r
+    //\r
+    if (msg_type_op->len != 1) {\r
+      return -1;\r
+    }\r
+\r
+    if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) {\r
+      return -1;\r
+    }\r
+    //\r
+    // There must be a server identifier option.\r
+    //\r
+    EfiStatus = find_opt (\r
+                  rx_pkt,\r
+                  DHCP4_SERVER_IDENTIFIER,\r
+                  0,\r
+                  &srvid_op\r
+                  );\r
+\r
+    if (EFI_ERROR (EfiStatus)) {\r
+      return -1;\r
+    }\r
+\r
+    if (srvid_op->len != 4) {\r
+      return -1;\r
+    }\r
+    //\r
+    // Good DHCP offer packet.\r
+    //\r
+    break;\r
+  }\r
+  //\r
+  // Good DHCP (or BOOTP) packet.  Cache it!\r
+  //\r
+  EfiStatus = gBS->AllocatePool (\r
+                    EfiBootServicesData,\r
+                    (Private->offers + 1) * sizeof (DHCP4_PACKET),\r
+                    (VOID **) &tmp\r
+                    );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    return -2;\r
+  }\r
+\r
+  ASSERT (tmp);\r
+\r
+  if (Private->offers != 0) {\r
+    CopyMem (\r
+      tmp,\r
+      Private->offer_list,\r
+      Private->offers * sizeof (DHCP4_PACKET)\r
+      );\r
+\r
+    gBS->FreePool (Private->offer_list);\r
+  }\r
+\r
+  CopyMem (&tmp[Private->offers++], rx_pkt, sizeof (DHCP4_PACKET));\r
+\r
+  Private->offer_list = tmp;\r
+\r
+  return 0;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+**/\r
+STATIC\r
+INTN\r
+acknak_verify (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN DHCP4_PACKET           *tx_pkt,\r
+  IN DHCP4_PACKET           *rx_pkt,\r
+  IN UINTN                  rx_pkt_size\r
+  )\r
+{\r
+  EFI_STATUS  EfiStatus;\r
+  DHCP4_OP    *msg_type_op;\r
+  DHCP4_OP    *srvid_op;\r
+  DHCP4_OP    *renew_op;\r
+  DHCP4_OP    *rebind_op;\r
+  DHCP4_OP    *lease_time_op;\r
+  UINT32      magik;\r
+\r
+  //\r
+  // Verify parameters.  Touch unused parameters to\r
+  // keep compiler happy.\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (rx_pkt);\r
+\r
+  if (Private == NULL || rx_pkt == NULL) {\r
+    return -2;\r
+  }\r
+\r
+  tx_pkt      = tx_pkt;\r
+  rx_pkt_size = rx_pkt_size;\r
+\r
+  //\r
+  // This must be a DHCP Ack message.\r
+  //\r
+  magik = htonl (DHCP4_MAGIK_NUMBER);\r
+\r
+  if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {\r
+    return -1;\r
+  }\r
+\r
+  EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    return -1;\r
+  }\r
+\r
+  if (msg_type_op->len != 1) {\r
+    return -1;\r
+  }\r
+\r
+  if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) {\r
+    return -1;\r
+  }\r
+  //\r
+  // There must be a server identifier.\r
+  //\r
+  EfiStatus = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    return -1;\r
+  }\r
+\r
+  if (srvid_op->len != 4) {\r
+    return -1;\r
+  }\r
+  //\r
+  // There should be a renewal time.\r
+  // If there is not, we will default to the 7/8 of the rebinding time.\r
+  //\r
+  EfiStatus = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    renew_op = NULL;\r
+  } else if (renew_op->len != 4) {\r
+    renew_op = NULL;\r
+  }\r
+  //\r
+  // There should be a rebinding time.\r
+  // If there is not, we will default to 7/8 of the lease time.\r
+  //\r
+  EfiStatus = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    rebind_op = NULL;\r
+  } else if (rebind_op->len != 4) {\r
+    rebind_op = NULL;\r
+  }\r
+  //\r
+  // There should be a lease time.\r
+  // If there is not, we will default to one week.\r
+  //\r
+  EfiStatus = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    lease_time_op = NULL;\r
+  } else if (lease_time_op->len != 4) {\r
+    lease_time_op = NULL;\r
+  }\r
+  //\r
+  // Packet looks good.  Double check the renew, rebind and lease times.\r
+  //\r
+  CopyMem (&Private->ServerIp, srvid_op->data, 4);\r
+\r
+  if (renew_op != NULL) {\r
+    CopyMem (&Private->RenewTime, renew_op->data, 4);\r
+    Private->RenewTime = htonl (Private->RenewTime);\r
+  } else {\r
+    Private->RenewTime = 0;\r
+  }\r
+\r
+  if (rebind_op != NULL) {\r
+    CopyMem (&Private->RebindTime, rebind_op->data, 4);\r
+    Private->RebindTime = htonl (Private->RebindTime);\r
+  } else {\r
+    Private->RebindTime = 0;\r
+  }\r
+\r
+  if (lease_time_op != NULL) {\r
+    CopyMem (&Private->LeaseTime, lease_time_op->data, 4);\r
+    Private->LeaseTime = htonl (Private->LeaseTime);\r
+  } else {\r
+    Private->LeaseTime = 0;\r
+  }\r
+\r
+  if (Private->LeaseTime < 60) {\r
+    Private->LeaseTime = 7 * 86400;\r
+  }\r
+\r
+  if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) {\r
+    Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8;\r
+  }\r
+\r
+  if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) {\r
+    Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4Init (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN UINTN                  seconds_timeout,\r
+  OUT UINTN                 *Offers,\r
+  OUT DHCP4_PACKET          **OfferList\r
+  )\r
+{\r
+  PXE_DHCP4_PRIVATE_DATA  *Private;\r
+  DHCP4_PACKET            offer;\r
+  EFI_IP_ADDRESS          bcast_ip;\r
+  EFI_STATUS              EfiStatus;\r
+\r
+  //\r
+  // Verify parameters and protocol state.\r
+  //\r
+  if (This == NULL ||\r
+      seconds_timeout < DHCP4_MIN_SECONDS ||\r
+      seconds_timeout > DHCP4_MAX_SECONDS ||\r
+      Offers == NULL ||\r
+      OfferList == NULL\r
+      ) {\r
+    //\r
+    // Return parameters are not initialized when\r
+    // parameters are invalid!\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *Offers     = 0;\r
+  *OfferList  = NULL;\r
+\r
+  //\r
+  // Check protocol state.\r
+  //\r
+  if (This->Data == NULL) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (!This->Data->SetupCompleted) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+#if 0\r
+  if (!is_good_discover (&This->Data->Discover)) {\r
+    //\r
+    // %%TBD - check discover packet fields\r
+    //\r
+  }\r
+#endif\r
+  //\r
+  // Get pointer to our instance data.\r
+  //\r
+  Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+  if (Private == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Setup variables...\r
+  //\r
+  Private->offers     = 0;\r
+  Private->offer_list = NULL;\r
+\r
+  EfiStatus = gBS->HandleProtocol (\r
+                    Private->Handle,\r
+                    &gEfiPxeDhcp4CallbackProtocolGuid,\r
+                    (VOID *) &Private->callback\r
+                    );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    Private->callback = NULL;\r
+  }\r
+\r
+  Private->function = EFI_PXE_DHCP4_FUNCTION_INIT;\r
+\r
+  //\r
+  // Increment the transaction ID.\r
+  //\r
+  {\r
+    UINT32  xid;\r
+\r
+    CopyMem (&xid, &This->Data->Discover.dhcp4.xid, sizeof (UINT32));\r
+\r
+    xid = htonl (htonl (xid) + 1);\r
+\r
+    CopyMem (&This->Data->Discover.dhcp4.xid, &xid, sizeof (UINT32));\r
+  }\r
+  //\r
+  // Transmit discover and wait for offers...\r
+  //\r
+  SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);\r
+\r
+  EfiStatus = tx_rx_udp (\r
+                Private,\r
+                &bcast_ip,\r
+                NULL,\r
+                NULL,\r
+                NULL,\r
+                &This->Data->Discover,\r
+                &offer,\r
+                &offer_verify,\r
+                seconds_timeout\r
+                );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    if (Private->offer_list) {\r
+      gBS->FreePool (Private->offer_list);\r
+    }\r
+\r
+    Private->offers     = 0;\r
+    Private->offer_list = NULL;\r
+    Private->callback   = NULL;\r
+\r
+    DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));\r
+    return EfiStatus;\r
+  }\r
+\r
+  *Offers                     = Private->offers;\r
+  *OfferList                  = Private->offer_list;\r
+\r
+  Private->offers             = 0;\r
+  Private->offer_list         = NULL;\r
+  Private->callback           = NULL;\r
+\r
+  This->Data->InitCompleted   = TRUE;\r
+  This->Data->SelectCompleted = FALSE;\r
+  This->Data->IsBootp         = FALSE;\r
+  This->Data->IsAck           = FALSE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4Select (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN UINTN                  seconds_timeout,\r
+  IN DHCP4_PACKET           *Offer\r
+  )\r
+{\r
+  PXE_DHCP4_PRIVATE_DATA  *Private;\r
+  EFI_STATUS              EfiStatus;\r
+  DHCP4_PACKET            request;\r
+  DHCP4_PACKET            acknak;\r
+  EFI_IP_ADDRESS          bcast_ip;\r
+  EFI_IP_ADDRESS          zero_ip;\r
+  EFI_IP_ADDRESS          local_ip;\r
+  DHCP4_OP                *srvid;\r
+  DHCP4_OP                *op;\r
+  UINT32                  dhcp4_magik;\r
+  UINT8                   buf[16];\r
+  BOOLEAN                 is_bootp;\r
+\r
+  //\r
+  // Verify parameters.\r
+  //\r
+  if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS || Offer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Check protocol state.\r
+  //\r
+  if (This->Data == NULL) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (!This->Data->SetupCompleted) {\r
+    return EFI_NOT_READY;\r
+  }\r
+  //\r
+  // Get pointer to instance data.\r
+  //\r
+  Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+  if (Private == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+#if 0\r
+  if (!is_good_discover (&This->Data->Discover)) {\r
+    //\r
+    // %%TBD - check discover packet fields\r
+    //\r
+  }\r
+#endif\r
+  //\r
+  // Setup useful variables...\r
+  //\r
+  SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);\r
+\r
+  ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));\r
+\r
+  ZeroMem (&local_ip, sizeof (EFI_IP_ADDRESS));\r
+  local_ip.v4.Addr[0]         = 127;\r
+  local_ip.v4.Addr[3]         = 1;\r
+\r
+  This->Data->SelectCompleted = FALSE;\r
+  This->Data->IsBootp         = FALSE;\r
+  This->Data->IsAck           = FALSE;\r
+\r
+  EfiStatus = gBS->HandleProtocol (\r
+                    Private->Handle,\r
+                    &gEfiPxeDhcp4CallbackProtocolGuid,\r
+                    (VOID *) &Private->callback\r
+                    );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    Private->callback = NULL;\r
+  }\r
+\r
+  Private->function = EFI_PXE_DHCP4_FUNCTION_SELECT;\r
+\r
+  //\r
+  // Verify offer packet fields.\r
+  //\r
+  if (Offer->dhcp4.op != BOOTP_REPLY) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Offer->dhcp4.htype != This->Data->Discover.dhcp4.htype) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Offer->dhcp4.hlen != This->Data->Discover.dhcp4.hlen) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (CompareMem (&Offer->dhcp4.xid, &This->Data->Discover.dhcp4.xid, 4)) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!CompareMem (&Offer->dhcp4.yiaddr, &bcast_ip, 4)) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!CompareMem (&Offer->dhcp4.yiaddr, &zero_ip, 4)) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!CompareMem (&Offer->dhcp4.yiaddr, &local_ip, 4)) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (CompareMem (\r
+        &Offer->dhcp4.chaddr,\r
+        &This->Data->Discover.dhcp4.chaddr,\r
+        16\r
+        )) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // DHCP option checks\r
+  //\r
+  dhcp4_magik = htonl (DHCP4_MAGIK_NUMBER);\r
+  is_bootp    = TRUE;\r
+\r
+  if (!CompareMem (&Offer->dhcp4.magik, &dhcp4_magik, 4)) {\r
+    //\r
+    // If present, DHCP message type must be offer.\r
+    //\r
+    EfiStatus = find_opt (Offer, DHCP4_MESSAGE_TYPE, 0, &op);\r
+\r
+    if (!EFI_ERROR (EfiStatus)) {\r
+      if (op->len != 1 || op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) {\r
+        Private->callback = NULL;\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      is_bootp = FALSE;\r
+    }\r
+    //\r
+    // If present, DHCP max message size must be valid.\r
+    //\r
+    EfiStatus = find_opt (Offer, DHCP4_MAX_MESSAGE_SIZE, 0, &op);\r
+\r
+    if (!EFI_ERROR (EfiStatus)) {\r
+      if (op->len != 2 || ((op->data[0] << 8) | op->data[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) {\r
+        Private->callback = NULL;\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+    //\r
+    // If present, DHCP server identifier must be valid.\r
+    //\r
+    EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &op);\r
+\r
+    if (!EFI_ERROR (EfiStatus)) {\r
+      if (op->len != 4 || !CompareMem (op->data, &bcast_ip, 4) || !CompareMem (op->data, &zero_ip, 4)) {\r
+        Private->callback = NULL;\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+    //\r
+    // If present, DHCP subnet mask must be valid.\r
+    //\r
+    EfiStatus = find_opt (\r
+                  Offer,\r
+                  DHCP4_SUBNET_MASK,\r
+                  0,\r
+                  &op\r
+                  );\r
+\r
+    if (!EFI_ERROR (EfiStatus)) {\r
+      if (op->len != 4) {\r
+        Private->callback = NULL;\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // Early out for BOOTP.\r
+  //\r
+  This->Data->IsBootp = is_bootp;\r
+  if (is_bootp) {\r
+    //\r
+    // Copy offer packet to instance data.\r
+    //\r
+    CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET));\r
+\r
+    //\r
+    // Copy discover to request and offer to acknak.\r
+    //\r
+    CopyMem (\r
+      &This->Data->Request,\r
+      &This->Data->Discover,\r
+      sizeof (DHCP4_PACKET)\r
+      );\r
+\r
+    CopyMem (\r
+      &This->Data->AckNak,\r
+      &This->Data->Offer,\r
+      sizeof (DHCP4_PACKET)\r
+      );\r
+\r
+    //\r
+    // Set state flags.\r
+    //\r
+    This->Data->SelectCompleted = TRUE;\r
+    This->Data->IsAck           = TRUE;\r
+\r
+    Private->callback           = NULL;\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Copy discover packet contents to request packet.\r
+  //\r
+  CopyMem (&request, &This->Data->Discover, sizeof (DHCP4_PACKET));\r
+\r
+  This->Data->IsAck = FALSE;\r
+\r
+  //\r
+  // Change DHCP message type from discover to request.\r
+  //\r
+  EfiStatus = find_opt (&request, DHCP4_MESSAGE_TYPE, 0, &op);\r
+\r
+  if (EFI_ERROR (EfiStatus) && EfiStatus != EFI_NOT_FOUND) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (EfiStatus == EFI_NOT_FOUND) {\r
+    EfiStatus = find_opt (&request, DHCP4_END, 0, &op);\r
+\r
+    if (EFI_ERROR (EfiStatus)) {\r
+      Private->callback = NULL;\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    op->op      = DHCP4_MESSAGE_TYPE;\r
+    op->len     = 1;\r
+\r
+    op->data[1] = DHCP4_END;\r
+  }\r
+\r
+  op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST;\r
+\r
+  //\r
+  // Copy server identifier option from offer to request.\r
+  //\r
+  EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &srvid);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (srvid->len != 4) {\r
+    Private->callback = NULL;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EfiStatus = add_opt (&request, srvid);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));\r
+    Private->callback = NULL;\r
+    return EfiStatus;\r
+  }\r
+  //\r
+  // Add requested IP address option to request packet.\r
+  //\r
+  op      = (DHCP4_OP *) buf;\r
+  op->op  = DHCP4_REQUESTED_IP_ADDRESS;\r
+  op->len = 4;\r
+  CopyMem (op->data, &Offer->dhcp4.yiaddr, 4);\r
+\r
+  EfiStatus = add_opt (&request, op);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));\r
+    Private->callback = NULL;\r
+    return EfiStatus;\r
+  }\r
+  //\r
+  // Transimit DHCP request and wait for DHCP ack...\r
+  //\r
+  SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);\r
+\r
+  EfiStatus = tx_rx_udp (\r
+                Private,\r
+                &bcast_ip,\r
+                NULL,\r
+                NULL,\r
+                NULL,\r
+                &request,\r
+                &acknak,\r
+                &acknak_verify,\r
+                seconds_timeout\r
+                );\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));\r
+    Private->callback = NULL;\r
+    return EfiStatus;\r
+  }\r
+  //\r
+  // Set Data->IsAck and return.\r
+  //\r
+  EfiStatus = find_opt (&acknak, DHCP4_MESSAGE_TYPE, 0, &op);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    Private->callback = NULL;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (op->len != 1) {\r
+    Private->callback = NULL;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  switch (op->data[0]) {\r
+  case DHCP4_MESSAGE_TYPE_ACK:\r
+    This->Data->IsAck = TRUE;\r
+    break;\r
+\r
+  case DHCP4_MESSAGE_TYPE_NAK:\r
+    This->Data->IsAck = FALSE;\r
+    break;\r
+\r
+  default:\r
+    Private->callback = NULL;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Copy packets into instance data...\r
+  //\r
+  CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET));\r
+  CopyMem (&This->Data->Request, &request, sizeof (DHCP4_PACKET));\r
+  CopyMem (&This->Data->AckNak, &acknak, sizeof (DHCP4_PACKET));\r
+\r
+  This->Data->SelectCompleted = TRUE;\r
+\r
+  Private->callback           = NULL;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* eof - PxeDhcp4InitSelect.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c
new file mode 100644 (file)
index 0000000..119acff
--- /dev/null
@@ -0,0 +1,247 @@
+/** @file\r
+\r
+Copyright (c) 2004, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeDhcp4Release.c\r
+\r
+Abstract:\r
+  Transmit release packet, free allocations and shutdown PxeDhcp4.\r
+\r
+\r
+**/\r
+\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4Release (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This\r
+  )\r
+{\r
+  PXE_DHCP4_PRIVATE_DATA  *Private;\r
+  EFI_IP_ADDRESS          ServerIp;\r
+  EFI_IP_ADDRESS          client_ip;\r
+  EFI_IP_ADDRESS          gateway_ip;\r
+  EFI_IP_ADDRESS          subnet_mask;\r
+  EFI_STATUS              efi_status;\r
+  DHCP4_OP                *op;\r
+  UINT8                   op_list[20];\r
+\r
+  //\r
+  // Check for invalid parameters.\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Release does nothing if the protocol has never been setup.\r
+  //\r
+  if (This->Data == NULL) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+  //\r
+  // Fail if we do not have valid instance data.\r
+  //\r
+  Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+  if (Private == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // If this is a BOOTP session and there is not a DHCP Ack\r
+  // packet, just release storage and return.\r
+  //\r
+  if (This->Data->IsBootp || !This->Data->IsAck) {\r
+    gBS->FreePool (This->Data);\r
+    This->Data = NULL;\r
+\r
+    if (Private->StopPxeBc) {\r
+      Private->PxeBc->Stop (Private->PxeBc);\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Build option list for DHCP Release packet.\r
+  // If any errors occur, just release storage and return.\r
+  //\r
+  //\r
+  // Message type is first.\r
+  //\r
+  op_list[0]  = DHCP4_MESSAGE_TYPE;\r
+  op_list[1]  = 1;\r
+  op_list[2]  = DHCP4_MESSAGE_TYPE_RELEASE;\r
+\r
+  //\r
+  // Followed by server identifier.\r
+  //\r
+  efi_status = find_opt (\r
+                &This->Data->Request,\r
+                DHCP4_SERVER_IDENTIFIER,\r
+                0,\r
+                &op\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    gBS->FreePool (This->Data);\r
+    This->Data = NULL;\r
+\r
+    if (Private->StopPxeBc) {\r
+      Private->PxeBc->Stop (Private->PxeBc);\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (op->len != 4) {\r
+    gBS->FreePool (This->Data);\r
+    This->Data = NULL;\r
+\r
+    if (Private->StopPxeBc) {\r
+      Private->PxeBc->Stop (Private->PxeBc);\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  CopyMem (&ServerIp, op->data, 4);\r
+\r
+  op_list[3]  = DHCP4_SERVER_IDENTIFIER;\r
+  op_list[4]  = 4;\r
+  CopyMem (&op_list[5], &ServerIp, 4);\r
+\r
+  //\r
+  // Followed by end.\r
+  //\r
+  op_list[9] = DHCP4_END;\r
+\r
+  //\r
+  // We need a subnet mask for IP stack operation.\r
+  //\r
+  efi_status = find_opt (\r
+                &This->Data->AckNak,\r
+                DHCP4_SUBNET_MASK,\r
+                0,\r
+                &op\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    gBS->FreePool (This->Data);\r
+    This->Data = NULL;\r
+\r
+    if (Private->StopPxeBc) {\r
+      Private->PxeBc->Stop (Private->PxeBc);\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (op->len != 4) {\r
+    gBS->FreePool (This->Data);\r
+    This->Data = NULL;\r
+\r
+    if (Private->StopPxeBc) {\r
+      Private->PxeBc->Stop (Private->PxeBc);\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  ZeroMem (&subnet_mask, sizeof (EFI_IP_ADDRESS));\r
+  CopyMem (&subnet_mask, op->data, 4);\r
+\r
+  //\r
+  // Gateway IP address may be needed.\r
+  //\r
+  ZeroMem (&gateway_ip, sizeof (EFI_IP_ADDRESS));\r
+  CopyMem (&gateway_ip, &This->Data->AckNak.dhcp4.giaddr, 4);\r
+\r
+  //\r
+  // Client IP address needed for IP stack operation.\r
+  //\r
+  ZeroMem (&client_ip, sizeof (EFI_IP_ADDRESS));\r
+  CopyMem (&client_ip, &This->Data->AckNak.dhcp4.yiaddr, 4);\r
+\r
+  //\r
+  // Enable UDP...\r
+  //\r
+  efi_status = start_udp (Private, &client_ip, &subnet_mask);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    gBS->FreePool (This->Data);\r
+    This->Data = NULL;\r
+\r
+    if (Private->StopPxeBc) {\r
+      Private->PxeBc->Stop (Private->PxeBc);\r
+    }\r
+\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Gather information out of DHCP request packet needed for\r
+  // DHCP release packet.\r
+  //\r
+  //\r
+  // Setup DHCP Release packet.\r
+  //\r
+  CopyMem (&This->Data->Request.dhcp4.ciaddr, &client_ip, 4);\r
+\r
+  ZeroMem (&This->Data->Request.dhcp4.yiaddr, 12);\r
+\r
+  ZeroMem (&This->Data->Request.dhcp4.sname, 64 + 128);\r
+\r
+  This->Data->Request.dhcp4.hops  = 0;\r
+  This->Data->Request.dhcp4.secs  = 0;\r
+  This->Data->Request.dhcp4.flags = 0;\r
+\r
+  ZeroMem (\r
+    &This->Data->Request.dhcp4.options,\r
+    sizeof This->Data->Request.dhcp4.options\r
+    );\r
+\r
+  CopyMem (&This->Data->Request.dhcp4.options, op_list, 10);\r
+\r
+  //\r
+  // Transmit DHCP Release packet.\r
+  //\r
+  tx_udp (\r
+    Private,\r
+    &ServerIp,\r
+    &gateway_ip,\r
+    &client_ip,\r
+    &This->Data->Request,\r
+    DHCP4_MAX_PACKET_SIZE - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE)\r
+    );\r
+\r
+  gBS->Stall (1000000); /* 1/10th second */\r
+\r
+  //\r
+  // Shutdown PXE BaseCode and release local storage.\r
+  //\r
+  stop_udp (Private);\r
+\r
+  gBS->FreePool (This->Data);\r
+  This->Data = NULL;\r
+\r
+  if (Private->StopPxeBc) {\r
+    Private->PxeBc->Stop (Private->PxeBc);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* eof - PxeDhcp4Release.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c
new file mode 100644 (file)
index 0000000..193bb18
--- /dev/null
@@ -0,0 +1,408 @@
+/** @file\r
+\r
+Copyright (c) 2004, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeDhcp4RenewRebind.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+  Parameters:\r
+\r
+  @return -2 = ignore, stop waiting\r
+  @return -1 = ignore, keep waiting\r
+  @return 0 = accept, keep waiting\r
+  @return 1 = accept, stop waiting\r
+\r
+**/\r
+STATIC\r
+INTN\r
+acknak_verify (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN DHCP4_PACKET           *tx_pkt,\r
+  IN DHCP4_PACKET           *rx_pkt,\r
+  IN UINTN                  rx_pkt_size\r
+  )\r
+{\r
+  EFI_STATUS  efi_status;\r
+  DHCP4_OP    *msg_type_op;\r
+  DHCP4_OP    *srvid_op;\r
+  DHCP4_OP    *renew_op;\r
+  DHCP4_OP    *rebind_op;\r
+  DHCP4_OP    *lease_time_op;\r
+  UINT32      magik;\r
+\r
+  //\r
+  // Verify parameters.  Unused parameters are also touched\r
+  // to make the compiler happy.\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (rx_pkt);\r
+\r
+  if (Private == NULL || rx_pkt == NULL) {\r
+    return -2;\r
+  }\r
+\r
+  tx_pkt      = tx_pkt;\r
+  rx_pkt_size = rx_pkt_size;\r
+\r
+  //\r
+  // This must be a DHCP Ack message.\r
+  //\r
+  magik = htonl (DHCP4_MAGIK_NUMBER);\r
+\r
+  if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {\r
+    return -1;\r
+  }\r
+\r
+  efi_status = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    return -1;\r
+  }\r
+\r
+  if (msg_type_op->len != 1) {\r
+    return -1;\r
+  }\r
+\r
+  if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) {\r
+    return -1;\r
+  }\r
+  //\r
+  // There must be a server identifier.\r
+  //\r
+  efi_status = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    return -1;\r
+  }\r
+\r
+  if (srvid_op->len != 4) {\r
+    return -1;\r
+  }\r
+  //\r
+  // There should be a renewal time.\r
+  // If there is not, we will default to the 7/8 of the rebinding time.\r
+  //\r
+  efi_status = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    renew_op = NULL;\r
+  } else if (renew_op->len != 4) {\r
+    renew_op = NULL;\r
+  }\r
+  //\r
+  // There should be a rebinding time.\r
+  // If there is not, we will default to 7/8 of the lease time.\r
+  //\r
+  efi_status = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    rebind_op = NULL;\r
+  } else if (rebind_op->len != 4) {\r
+    rebind_op = NULL;\r
+  }\r
+  //\r
+  // There should be a lease time.\r
+  // If there is not, we will default to one week.\r
+  //\r
+  efi_status = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    lease_time_op = NULL;\r
+  } else if (lease_time_op->len != 4) {\r
+    lease_time_op = NULL;\r
+  }\r
+  //\r
+  // Packet looks good.  Double check the renew, rebind and lease times.\r
+  //\r
+  CopyMem (&Private->ServerIp, srvid_op->data, 4);\r
+\r
+  if (renew_op != NULL) {\r
+    CopyMem (&Private->RenewTime, renew_op->data, 4);\r
+    Private->RenewTime = htonl (Private->RenewTime);\r
+  } else {\r
+    Private->RenewTime = 0;\r
+  }\r
+\r
+  if (rebind_op != NULL) {\r
+    CopyMem (&Private->RebindTime, rebind_op->data, 4);\r
+    Private->RebindTime = htonl (Private->RebindTime);\r
+  } else {\r
+    Private->RebindTime = 0;\r
+  }\r
+\r
+  if (lease_time_op != NULL) {\r
+    CopyMem (&Private->LeaseTime, lease_time_op->data, 4);\r
+    Private->LeaseTime = htonl (Private->LeaseTime);\r
+  } else {\r
+    Private->LeaseTime = 0;\r
+  }\r
+\r
+  if (Private->LeaseTime < 60) {\r
+    Private->LeaseTime = 7 * 86400;\r
+  }\r
+\r
+  if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) {\r
+    Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8;\r
+  }\r
+\r
+  if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) {\r
+    Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+renew_rebind (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN UINTN                  seconds_timeout,\r
+  IN BOOLEAN                renew\r
+  )\r
+{\r
+  PXE_DHCP4_PRIVATE_DATA  *Private;\r
+  EFI_IP_ADDRESS          ServerIp;\r
+  EFI_IP_ADDRESS          client_ip;\r
+  EFI_IP_ADDRESS          subnet_mask;\r
+  EFI_IP_ADDRESS          gateway_ip;\r
+  DHCP4_PACKET            Request;\r
+  DHCP4_PACKET            AckNak;\r
+  DHCP4_OP                *op;\r
+  EFI_STATUS              efi_status;\r
+\r
+  //\r
+  // Check for invalid parameters.\r
+  //\r
+  if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Check for proper protocol state.\r
+  //\r
+  if (This->Data == NULL) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (!This->Data->SelectCompleted) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  if (This->Data->IsBootp) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (!This->Data->IsAck) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Get pointer to instance data.\r
+  //\r
+  Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+  if (Private == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Copy Discover packet to temporary request packet\r
+  // to be used for Renew/Rebind operation.\r
+  //\r
+  CopyMem (&Request, &This->Data->Discover, sizeof (DHCP4_PACKET));\r
+\r
+  CopyMem (&Request.dhcp4.ciaddr, &This->Data->AckNak.dhcp4.yiaddr, 4);\r
+\r
+  Request.dhcp4.flags = 0;  /* Reply does not need to be broadcast. */\r
+\r
+  //\r
+  // Change message type from discover to request.\r
+  //\r
+  efi_status = find_opt (&Request, DHCP4_MESSAGE_TYPE, 0, &op);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (op->len != 1) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST;\r
+\r
+  //\r
+  // Need a subnet mask.\r
+  //\r
+  efi_status = find_opt (\r
+                &This->Data->AckNak,\r
+                DHCP4_SUBNET_MASK,\r
+                0,\r
+                &op\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (op->len != 4) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ZeroMem (&subnet_mask, sizeof (EFI_IP_ADDRESS));\r
+  CopyMem (&subnet_mask, op->data, 4);\r
+\r
+  //\r
+  // Need a server IP address (renew) or a broadcast\r
+  // IP address (rebind).\r
+  //\r
+  ZeroMem (&gateway_ip, sizeof (EFI_IP_ADDRESS));\r
+\r
+  if (renew) {\r
+    efi_status = find_opt (\r
+                  &This->Data->AckNak,\r
+                  DHCP4_SERVER_IDENTIFIER,\r
+                  0,\r
+                  &op\r
+                  );\r
+\r
+    if (EFI_ERROR (efi_status)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (op->len != 4) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS));\r
+    CopyMem (&ServerIp, op->data, 4);\r
+\r
+    //\r
+    //\r
+    //\r
+    if (CompareMem (&This->Data->AckNak.dhcp4.giaddr, &gateway_ip, 4)) {\r
+      CopyMem (&gateway_ip, &This->Data->AckNak.dhcp4.giaddr, 4);\r
+    }\r
+  } else {\r
+    SetMem (&ServerIp, sizeof (EFI_IP_ADDRESS), 0xFF);\r
+  }\r
+  //\r
+  // Need a client IP address.\r
+  //\r
+  ZeroMem (&client_ip, sizeof (EFI_IP_ADDRESS));\r
+  CopyMem (&client_ip, &Request.dhcp4.ciaddr, 4);\r
+\r
+  //\r
+  //\r
+  //\r
+  efi_status = gBS->HandleProtocol (\r
+                      Private->Handle,\r
+                      &gEfiPxeDhcp4CallbackProtocolGuid,\r
+                      (VOID *) &Private->callback\r
+                      );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    Private->callback = NULL;\r
+  }\r
+\r
+  Private->function = renew ? EFI_PXE_DHCP4_FUNCTION_RENEW : EFI_PXE_DHCP4_FUNCTION_REBIND;\r
+\r
+  //\r
+  // Transimit DHCP request and wait for DHCP ack...\r
+  //\r
+  efi_status = tx_rx_udp (\r
+                Private,\r
+                &ServerIp,\r
+                &gateway_ip,\r
+                &client_ip,\r
+                &subnet_mask,\r
+                &Request,\r
+                &AckNak,\r
+                &acknak_verify,\r
+                seconds_timeout\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    Private->callback = NULL;\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Copy server identifier, renewal time and rebinding time\r
+  // from temporary ack/nak packet into cached ack/nak packet.\r
+  //\r
+  efi_status = find_opt (\r
+                &This->Data->AckNak,\r
+                DHCP4_SERVER_IDENTIFIER,\r
+                0,\r
+                &op\r
+                );\r
+\r
+  if (!EFI_ERROR (efi_status)) {\r
+    if (op->len == 4) {\r
+      CopyMem (op->data, &Private->ServerIp, 4);\r
+    }\r
+  }\r
+\r
+  efi_status = find_opt (&This->Data->AckNak, DHCP4_RENEWAL_TIME, 0, &op);\r
+\r
+  if (!EFI_ERROR (efi_status)) {\r
+    if (op->len == 4) {\r
+      CopyMem (op->data, &Private->RenewTime, 4);\r
+    }\r
+  }\r
+\r
+  efi_status = find_opt (&This->Data->AckNak, DHCP4_REBINDING_TIME, 0, &op);\r
+\r
+  if (!EFI_ERROR (efi_status)) {\r
+    if (op->len == 4) {\r
+      CopyMem (op->data, &Private->RebindTime, 4);\r
+    }\r
+  }\r
+\r
+  Private->callback = NULL;\r
+  return efi_status;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4Renew (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN UINTN                  seconds_timeout\r
+  )\r
+{\r
+  return renew_rebind (This, seconds_timeout, TRUE);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4Rebind (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN UINTN                  seconds_timeout\r
+  )\r
+{\r
+  return renew_rebind (This, seconds_timeout, FALSE);\r
+}\r
+\r
+/* eof - PxeDhcp4RenewRebind.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c
new file mode 100644 (file)
index 0000000..e796d12
--- /dev/null
@@ -0,0 +1,191 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeDhcp4Run.c\r
+\r
+Abstract:\r
+  Simplified entry point for starting basic PxeDhcp4 client operation.\r
+\r
+\r
+**/\r
+\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4Run (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN OPTIONAL UINTN                  OpLen,\r
+  IN OPTIONAL VOID                   *OpList\r
+  )\r
+{\r
+  PXE_DHCP4_PRIVATE_DATA  *Private;\r
+  DHCP4_PACKET            *offer_list;\r
+  EFI_STATUS              efi_status;\r
+  EFI_IP_ADDRESS          zero_ip;\r
+  UINTN                   offers;\r
+  UINTN                   timeout;\r
+  UINTN                   n;\r
+  UINT16                  seconds;\r
+\r
+  //\r
+  // Validate parameters.\r
+  //\r
+  if (This == NULL || (OpLen != 0 && OpList == NULL) || (OpLen == 0 && OpList != NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  for (n = 0; n < OpLen;) {\r
+    switch (((UINT8 *) OpList)[n]) {\r
+    case DHCP4_PAD:\r
+      ++n;\r
+      continue;\r
+\r
+    case DHCP4_END:\r
+      ++n;\r
+      break;\r
+\r
+    default:\r
+      n += 2 + ((UINT8 *) OpList)[n + 1];\r
+      continue;\r
+    }\r
+\r
+    break;\r
+  }\r
+\r
+  if (n != OpLen) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Get pointer to instance data.\r
+  //\r
+  Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+  if (Private == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Initialize DHCP discover packet.\r
+  //\r
+  efi_status = PxeDhcp4Setup (This, NULL);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    return efi_status;\r
+  }\r
+\r
+  for (n = 0; n < OpLen;) {\r
+    switch (((UINT8 *) OpList)[n]) {\r
+    case DHCP4_PAD:\r
+      ++n;\r
+      continue;\r
+\r
+    case DHCP4_END:\r
+      ++n;\r
+      break;\r
+\r
+    default:\r
+      efi_status = add_opt (\r
+                    &This->Data->Discover,\r
+                    (DHCP4_OP *) &(((UINT8 *) OpList)[n])\r
+                    );\r
+\r
+      if (EFI_ERROR (efi_status)) {\r
+        return efi_status;\r
+      }\r
+\r
+      n += 2 + ((UINT8 *) OpList)[n + 1];\r
+      continue;\r
+    }\r
+\r
+    break;\r
+  }\r
+  //\r
+  // Basic DHCP D.O.R.A.\r
+  // 1, 2, 4, 8, 16 & 32 second timeouts.\r
+  // Callback routine can be used to break out earlier.\r
+  //\r
+  ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));\r
+\r
+  for (timeout = 1;;) {\r
+    //\r
+    // Broadcast DHCP discover and wait for DHCP offers.\r
+    //\r
+    efi_status = PxeDhcp4Init (This, timeout, &offers, &offer_list);\r
+\r
+    if ((efi_status != EFI_SUCCESS) &&\r
+        (efi_status != EFI_TIMEOUT) &&\r
+        (efi_status != EFI_NO_RESPONSE)) {\r
+      return efi_status;\r
+    }\r
+    //\r
+    // Try to select from each DHCP or BOOTP offer.\r
+    //\r
+    for (n = 0; n < offers; ++n) {\r
+      //\r
+      // Ignore proxyDHCP offers.\r
+      //\r
+      if (!CompareMem (&offer_list[n].dhcp4.yiaddr, &zero_ip, 4)) {\r
+        continue;\r
+      }\r
+      //\r
+      // Issue DHCP Request and wait for DHCP Ack/Nak.\r
+      //\r
+      efi_status = PxeDhcp4Select (\r
+                    This,\r
+                    timeout,\r
+                    &offer_list[n]\r
+                    );\r
+\r
+      if (EFI_ERROR (efi_status)) {\r
+        continue;\r
+      }\r
+      //\r
+      // Exit when we have got our DHCP Ack.\r
+      //\r
+      if (This->Data->IsAck) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+    //\r
+    // No DHCP Acks.  Release DHCP Offer list storage.\r
+    //\r
+    if (offer_list != NULL) {\r
+      gBS->FreePool (offer_list);\r
+      offer_list = NULL;\r
+    }\r
+    //\r
+    // Try again until we have used up >= DHCP4_MAX_SECONDS.\r
+    //\r
+    if ((timeout <<= 1) > DHCP4_MAX_SECONDS) {\r
+      if (!EFI_ERROR (efi_status)) {\r
+        efi_status = EFI_TIMEOUT;\r
+      }\r
+\r
+      return efi_status;\r
+    }\r
+    //\r
+    // Next timeout value.\r
+    //\r
+    CopyMem (&seconds, &This->Data->Discover.dhcp4.secs, 2);\r
+\r
+    seconds = htons (htons (seconds) + timeout);\r
+\r
+    CopyMem (&This->Data->Discover.dhcp4.secs, &seconds, 2);\r
+  }\r
+}\r
+\r
+/* eof - PxeDhcp4Run.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c
new file mode 100644 (file)
index 0000000..29e4ed0
--- /dev/null
@@ -0,0 +1,258 @@
+/** @file\r
+\r
+Copyright (c) 2004, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  PxeDhcp4Setup.c\r
+\r
+Abstract:\r
+\r
+\r
+**/\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+EFIAPI\r
+PxeDhcp4Setup (\r
+  IN EFI_PXE_DHCP4_PROTOCOL *This,\r
+  IN EFI_PXE_DHCP4_DATA     *Data\r
+  )\r
+{\r
+  PXE_DHCP4_PRIVATE_DATA  *Private;\r
+  DHCP4_HEADER            *Packet;\r
+  EFI_STATUS              EfiStatus;\r
+  UINT8                   *OpLen;\r
+  UINT8                   *OpPtr;\r
+\r
+  //\r
+  // Return error if parameters are invalid.\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+  if (Private == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (This->Data != NULL) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Check contents of provided Data structure.\r
+  //\r
+  if (Data != NULL) {\r
+    //\r
+    // Do protocol state checks first.\r
+    //\r
+    if (Data->SelectCompleted) {\r
+      if (!Data->InitCompleted || !Data->SetupCompleted) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      if (Data->IsBootp && !Data->IsAck) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    } else if (Data->InitCompleted) {\r
+      if (!Data->SetupCompleted || Data->IsBootp || Data->IsAck) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    } else if (Data->SetupCompleted) {\r
+      if (Data->IsBootp || Data->IsAck) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+    //\r
+    // Do packet content checks.\r
+    //\r
+    if (Data->SetupCompleted) {\r
+      //\r
+      // %%TBD - check discover packet\r
+      //\r
+    }\r
+\r
+    if (Data->SelectCompleted) {\r
+      if (Data->IsBootp) {\r
+        //\r
+        // %%TBD - check offer packet\r
+        //\r
+        if (CompareMem (\r
+              &Data->Discover,\r
+              &Data->Request,\r
+              sizeof (DHCP4_PACKET)\r
+              )) {\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+\r
+        if (CompareMem (\r
+              &Data->Offer,\r
+              &Data->AckNak,\r
+              sizeof (DHCP4_PACKET)\r
+              )) {\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+      } else {\r
+        //\r
+        // %%TBD - check offer, request & acknak packets\r
+        //\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // Allocate data structure.  Return error\r
+  // if there is not enough available memory.\r
+  //\r
+  This->Data = AllocatePool (sizeof (EFI_PXE_DHCP4_DATA));\r
+  if (This->Data == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Start PxeBc because we want to use its UdpWrite, UdpRead and\r
+  // SetFilter calls.\r
+  //\r
+  EfiStatus = Private->PxeBc->Start (Private->PxeBc, FALSE);\r
+\r
+  if (EFI_ERROR (EfiStatus)) {\r
+    if (EfiStatus != EFI_ALREADY_STARTED) {\r
+      FreePool (This->Data);\r
+      This->Data = NULL;\r
+      Private->PxeBc->Stop (Private->PxeBc);\r
+      return EfiStatus;\r
+    }\r
+\r
+    Private->StopPxeBc = FALSE;\r
+  } else {\r
+    Private->StopPxeBc = TRUE;\r
+  }\r
+  //\r
+  // Use new data.\r
+  //\r
+  if (Data != NULL) {\r
+    CopyMem (This->Data, Data, sizeof (EFI_PXE_DHCP4_DATA));\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Initialize new public data structure.\r
+  //\r
+  ZeroMem (This->Data, sizeof (EFI_PXE_DHCP4_DATA));\r
+\r
+  //\r
+  // Fill in default DHCP discover packet.\r
+  // Check for MAC addresses of strange lengths, just in case.\r
+  //\r
+  Packet        = &This->Data->Discover.dhcp4;\r
+\r
+  Packet->op    = BOOTP_REQUEST;\r
+\r
+  Packet->htype = Private->Snp->Mode->IfType;\r
+\r
+  if (Private->Snp->Mode->HwAddressSize > 16) {\r
+    Packet->hlen = 16;\r
+  } else {\r
+    Packet->hlen = (UINT8) Private->Snp->Mode->HwAddressSize;\r
+  }\r
+\r
+  Packet->hops = 0; /* Set to zero per RFC 2131. */\r
+\r
+  if (Packet->hlen < sizeof Packet->xid) {\r
+    if (Packet->hlen != 0) {\r
+      CopyMem (\r
+        &Packet->xid,\r
+        &Private->Snp->Mode->CurrentAddress,\r
+        Packet->hlen\r
+        );\r
+    }\r
+  } else {\r
+    CopyMem (\r
+      &Packet->xid,\r
+      &Private->Snp->Mode->CurrentAddress.Addr[Packet->hlen - sizeof Packet->xid],\r
+      sizeof Packet->xid\r
+      );\r
+  }\r
+  //\r
+  // %%TBD - xid should be randomized\r
+  //\r
+  Packet->secs  = htons (DHCP4_INITIAL_SECONDS);\r
+\r
+  Packet->flags = htons (DHCP4_BROADCAST_FLAG);\r
+\r
+  if (Packet->hlen != 0) {\r
+    CopyMem (Packet->chaddr, &Private->Snp->Mode->CurrentAddress, Packet->hlen);\r
+  }\r
+\r
+  Packet->magik               = htonl (DHCP4_MAGIK_NUMBER);\r
+\r
+  OpPtr                       = Packet->options;\r
+\r
+  *OpPtr++                    = DHCP4_MESSAGE_TYPE;\r
+  *OpPtr++                    = 1;\r
+  *OpPtr++                    = DHCP4_MESSAGE_TYPE_DISCOVER;\r
+\r
+  *OpPtr++                    = DHCP4_MAX_MESSAGE_SIZE;\r
+  *OpPtr++                    = 2;\r
+  *OpPtr++                    = (UINT8) ((DHCP4_DEFAULT_MAX_MESSAGE_SIZE >> 8) & 0xFF);\r
+  *OpPtr++                    = (UINT8) (DHCP4_DEFAULT_MAX_MESSAGE_SIZE & 0xFF);\r
+\r
+  *OpPtr++                    = DHCP4_PARAMETER_REQUEST_LIST;\r
+  OpLen                       = OpPtr;\r
+  *OpPtr++                    = 0;\r
+  *OpPtr++                    = DHCP4_SUBNET_MASK;\r
+  *OpPtr++                    = DHCP4_TIME_OFFSET;\r
+  *OpPtr++                    = DHCP4_ROUTER_LIST;\r
+  *OpPtr++                    = DHCP4_TIME_SERVERS;\r
+  *OpPtr++                    = DHCP4_NAME_SERVERS;\r
+  *OpPtr++                    = DHCP4_DNS_SERVERS;\r
+  *OpPtr++                    = DHCP4_HOST_NAME;\r
+  *OpPtr++                    = DHCP4_BOOT_FILE_SIZE;\r
+  *OpPtr++                    = DHCP4_MESSAGE_TYPE;\r
+  *OpPtr++                    = DHCP4_DOMAIN_NAME;\r
+  *OpPtr++                    = DHCP4_ROOT_PATH;\r
+  *OpPtr++                    = DHCP4_EXTENSION_PATH;\r
+  *OpPtr++                    = DHCP4_MAX_DATAGRAM_SIZE;\r
+  *OpPtr++                    = DHCP4_DEFAULT_TTL;\r
+  *OpPtr++                    = DHCP4_BROADCAST_ADDRESS;\r
+  *OpPtr++                    = DHCP4_NIS_DOMAIN_NAME;\r
+  *OpPtr++                    = DHCP4_NIS_SERVERS;\r
+  *OpPtr++                    = DHCP4_NTP_SERVERS;\r
+  *OpPtr++                    = DHCP4_VENDOR_SPECIFIC;\r
+  *OpPtr++                    = DHCP4_REQUESTED_IP_ADDRESS;\r
+  *OpPtr++                    = DHCP4_LEASE_TIME;\r
+  *OpPtr++                    = DHCP4_SERVER_IDENTIFIER;\r
+  *OpPtr++                    = DHCP4_RENEWAL_TIME;\r
+  *OpPtr++                    = DHCP4_REBINDING_TIME;\r
+  *OpPtr++                    = DHCP4_CLASS_IDENTIFIER;\r
+  *OpPtr++                    = DHCP4_TFTP_SERVER_NAME;\r
+  *OpPtr++                    = DHCP4_BOOTFILE;\r
+  *OpPtr++                    = 128;\r
+  *OpPtr++                    = 129;\r
+  *OpPtr++                    = 130;\r
+  *OpPtr++                    = 131;\r
+  *OpPtr++                    = 132;\r
+  *OpPtr++                    = 133;\r
+  *OpPtr++                    = 134;\r
+  *OpPtr++                    = 135;\r
+  *OpLen                      = (UINT8) ((OpPtr - OpLen) - 1);\r
+\r
+  *OpPtr++                    = DHCP4_END;\r
+\r
+  This->Data->SetupCompleted  = TRUE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* eof - PxeDhcp4Setup.c */\r
diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c
new file mode 100644 (file)
index 0000000..2e75052
--- /dev/null
@@ -0,0 +1,1086 @@
+/** @file\r
+\r
+Copyright (c) 2004 - 2005, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+  support.c\r
+\r
+Abstract:\r
+  Miscellaneous support routines for PxeDhcp4 protocol.\r
+\r
+\r
+**/\r
+\r
+\r
+#include "PxeDhcp4.h"\r
+\r
+#define DebugPrint(x)\r
+//\r
+// #define DebugPrint(x) Aprint x\r
+//\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+UINT16\r
+htons (\r
+  UINTN n\r
+  )\r
+{\r
+  return (UINT16) ((n >> 8) | (n << 8));\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+UINT32\r
+htonl (\r
+  UINTN n\r
+  )\r
+{\r
+  return (UINT32) ((n >> 24) | ((n >> 8) & 0xFF00) | ((n & 0xFF00) << 8) | (n << 24));\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+EFIAPI\r
+timeout_notify (\r
+  IN EFI_EVENT Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  ASSERT (Context);\r
+\r
+  if (Context != NULL) {\r
+    ((PXE_DHCP4_PRIVATE_DATA *) Context)->TimeoutOccurred = TRUE;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+EFIAPI\r
+periodic_notify (\r
+  IN EFI_EVENT Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  ASSERT (Context);\r
+\r
+  if (Context != NULL) {\r
+    ((PXE_DHCP4_PRIVATE_DATA *) Context)->PeriodicOccurred = TRUE;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_SUCCESS := Option was found\r
+  @return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL\r
+  @return EFI_INVALID_PARAMETER := OpCode == DHCP4_PAD\r
+  @return EFI_INVALID_PARAMETER := OpCode == DHCP4_END && Skip != 0\r
+  @return EFI_INVALID_PARAMETER := DHCP magik number in Packet is not valid\r
+  @return EFI_NOT_FOUND := op-code was not found in packet\r
+  @return EFI_INVALID_PARAMETER := If present, DHCP_MAX_MESSAGE_SIZE option\r
+  @return does not have a valid value.\r
+\r
+**/\r
+EFI_STATUS\r
+find_opt (\r
+  IN DHCP4_PACKET *Packet,\r
+  IN UINT8        OpCode,\r
+  IN UINTN        Skip,\r
+  OUT DHCP4_OP    **OpPtr\r
+  )\r
+{\r
+  UINTN msg_size;\r
+  UINTN buf_len;\r
+  UINTN n;\r
+  UINT8 *buf;\r
+  UINT8 *end_ptr;\r
+  UINT8 overload;\r
+\r
+  //\r
+  // Verify parameters.\r
+  //\r
+  if (Packet == NULL || OpPtr == NULL || OpCode == DHCP4_PAD || (OpCode == DHCP4_END && Skip != 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Packet->dhcp4.magik != htonl (DHCP4_MAGIK_NUMBER)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Initialize search variables.\r
+  //\r
+  *OpPtr    = NULL;\r
+\r
+  msg_size  = DHCP4_MAX_PACKET_SIZE - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);\r
+\r
+  overload  = 0;\r
+  end_ptr   = NULL;\r
+\r
+  buf       = Packet->dhcp4.options;\r
+  buf_len   = msg_size - (Packet->dhcp4.options - Packet->raw);\r
+\r
+  //\r
+  // Start searching for requested option.\r
+  //\r
+  for (n = 0;;) {\r
+    //\r
+    // If match is found, decrement skip count and return\r
+    // when desired match is found.\r
+    //\r
+    if (buf[n] == OpCode) {\r
+      *OpPtr = (DHCP4_OP *) &buf[n];\r
+\r
+      if (Skip-- == 0) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+    //\r
+    // Skip past current option.  Check for option overload\r
+    // and message size options since these will affect the\r
+    // amount of data to be searched.\r
+    //\r
+    switch (buf[n]) {\r
+    case DHCP4_PAD:\r
+      //\r
+      // Remember the first pad byte of a group.  This\r
+      // could be the end of a badly formed packet.\r
+      //\r
+      if (end_ptr == NULL) {\r
+        end_ptr = &buf[n];\r
+      }\r
+\r
+      ++n;\r
+      break;\r
+\r
+    case DHCP4_END:\r
+      //\r
+      // If we reach the end we are done.\r
+      //\r
+      end_ptr = NULL;\r
+      return EFI_NOT_FOUND;\r
+\r
+    case DHCP4_OPTION_OVERLOAD:\r
+      //\r
+      // Remember the option overload value since it\r
+      // could cause the search to continue into\r
+      // the fname and sname fields.\r
+      //\r
+      end_ptr = NULL;\r
+\r
+      if (buf[n + 1] == 1) {\r
+        overload = buf[n + 2];\r
+      }\r
+\r
+      n += 2 + buf[n + 1];\r
+      break;\r
+\r
+    case DHCP4_MAX_MESSAGE_SIZE:\r
+      //\r
+      // Remember the message size value since it could\r
+      // change the amount of option buffer to search.\r
+      //\r
+      end_ptr = NULL;\r
+\r
+      if (buf[n + 1] == 2 && buf == Packet->dhcp4.options) {\r
+        msg_size = ((buf[n + 2] << 8) | buf[n + 3]) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);\r
+\r
+        if (msg_size < 328) {\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+\r
+        buf_len = msg_size - (Packet->dhcp4.options - Packet->raw);\r
+\r
+        if (n + 2 + buf[n + 1] > buf_len) {\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+      }\r
+\r
+    /* fall thru */\r
+    default:\r
+      end_ptr = NULL;\r
+\r
+      n += 2 + buf[n + 1];\r
+    }\r
+    //\r
+    // Keep searching until the end of the buffer is reached.\r
+    //\r
+    if (n < buf_len) {\r
+      continue;\r
+    }\r
+    //\r
+    // Reached end of current buffer.  Check if we are supposed\r
+    // to search the fname and sname buffers.\r
+    //\r
+    if (buf == Packet->dhcp4.options &&\r
+        (overload == DHCP4_OVERLOAD_FNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)\r
+        ) {\r
+      buf     = Packet->dhcp4.fname;\r
+      buf_len = 128;\r
+      n       = 0;\r
+      continue;\r
+    }\r
+\r
+    if (buf != Packet->dhcp4.sname && (overload == DHCP4_OVERLOAD_SNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)) {\r
+      buf     = Packet->dhcp4.sname;\r
+      buf_len = 64;\r
+      n       = 0;\r
+      continue;\r
+    }\r
+    //\r
+    // End of last buffer reached.  If this was a search\r
+    // for the end of the options, go back to the start\r
+    // of the current pad block.\r
+    //\r
+    if (OpCode == DHCP4_END && end_ptr != NULL) {\r
+      *OpPtr = (DHCP4_OP *) end_ptr;\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    return EFI_NOT_FOUND;\r
+  }\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL\r
+  @return EFI_INVALID_PARAMETER := OpPtr->op == DHCP4_PAD || OpPtr->op == DHCP4_END\r
+  @return EFI_INVALID_PARAMETER := DHCP magik number in DHCP packet is not valid\r
+  @return EFI_INVALID_PARAMETER := If DHCP_MAX_MESSAGE_SIZE option is present and\r
+  @return is not valid\r
+  @return EFI_INVALID_PARAMETER := If DHCP_OPTION_OVERLOAD option is present and\r
+  @return is not valid\r
+  @return EFI_DEVICE_ERROR := Cannot determine end of packet\r
+  @return EFI_BUFFER_TOO_SMALL := Not enough room in packet to add option\r
+  @return EFI_SUCCESS := Option added to DHCP packet\r
+\r
+**/\r
+EFI_STATUS\r
+add_opt (\r
+  IN DHCP4_PACKET *Packet,\r
+  IN DHCP4_OP     *OpPtr\r
+  )\r
+{\r
+  EFI_STATUS  efi_status;\r
+  DHCP4_OP    *msg_size_op;\r
+  DHCP4_OP    *overload_op;\r
+  DHCP4_OP    *op;\r
+  UINTN       msg_size;\r
+  UINTN       buf_len;\r
+  UINT32      magik;\r
+  UINT8       *buf;\r
+\r
+  //\r
+  // Verify parameters.\r
+  //\r
+  ASSERT (Packet);\r
+  ASSERT (OpPtr);\r
+\r
+  if (Packet == NULL || OpPtr == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  switch (OpPtr->op) {\r
+  case DHCP4_PAD:\r
+  case DHCP4_END:\r
+    //\r
+    // No adding PAD or END.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Check the DHCP magik number.\r
+  //\r
+  CopyMem (&magik, &Packet->dhcp4.magik, 4);\r
+\r
+  if (magik != htonl (DHCP4_MAGIK_NUMBER)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Find the DHCP message size option.\r
+  //\r
+  msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE;\r
+\r
+  efi_status = find_opt (\r
+                Packet,\r
+                DHCP4_MAX_MESSAGE_SIZE,\r
+                0,\r
+                &msg_size_op\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    if (efi_status != EFI_NOT_FOUND) {\r
+      DebugPrint (\r
+        ("%s:%d:%r\n",\r
+        __FILE__,\r
+        __LINE__,\r
+        efi_status)\r
+        );\r
+      return efi_status;\r
+    }\r
+\r
+    msg_size_op = NULL;\r
+  } else {\r
+    CopyMem (&msg_size, msg_size_op->data, 2);\r
+    msg_size = htons (msg_size);\r
+\r
+    if (msg_size < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+  //\r
+  // Find the DHCP option overload option.\r
+  //\r
+  efi_status = find_opt (\r
+                Packet,\r
+                DHCP4_OPTION_OVERLOAD,\r
+                0,\r
+                &overload_op\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    if (efi_status != EFI_NOT_FOUND) {\r
+      DebugPrint (\r
+        ("%s:%d:%r\n",\r
+        __FILE__,\r
+        __LINE__,\r
+        efi_status)\r
+        );\r
+      return efi_status;\r
+    }\r
+\r
+    overload_op = NULL;\r
+  } else {\r
+    if (overload_op->len != 1) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    switch (overload_op->data[0]) {\r
+    case 1:\r
+    case 2:\r
+    case 3:\r
+      break;\r
+\r
+    default:\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+  //\r
+  // Find the end of the packet.\r
+  //\r
+  efi_status = find_opt (Packet, DHCP4_END, 0, &op);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Find which buffer the end is in.\r
+  //\r
+  if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.options)) {\r
+    buf_len = (msg_size - ((UINT8 *) &Packet->dhcp4.options - (UINT8 *) &Packet->raw)) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);\r
+  } else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.fname)) {\r
+    buf_len = 128;\r
+  } else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.sname)) {\r
+    buf_len = 64;\r
+  } else {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Add option to current buffer if there is no overlow.\r
+  //\r
+  if ((UINTN) ((&op->op - buf) + 3 + op->len) < buf_len) {\r
+    CopyMem (op, OpPtr, OpPtr->len + 2);\r
+\r
+    op->data[op->len] = DHCP4_END;\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Error if there is no space for option.\r
+  //\r
+  return EFI_BUFFER_TOO_SMALL;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_INVALID_PARAMETER := Private == NULL || Private->PxeBc == NULL\r
+  @return EFI_INVALID_PARAMETER := Only one of StationIp and SubnetMask is given\r
+  @return EFI_SUCCESS := UDP stack is ready\r
+  @return other := Error from PxeBc->SetIpFilter() or PxeBc->SetStationIp()\r
+\r
+**/\r
+EFI_STATUS\r
+start_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *StationIp,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *SubnetMask\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_IP_FILTER bcast_filter;\r
+  EFI_STATUS                  efi_status;\r
+\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (Private->PxeBc);\r
+\r
+  if (Private == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (StationIp != NULL && SubnetMask == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (StationIp == NULL && SubnetMask != NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Setup broadcast receive filter...\r
+  //\r
+  ZeroMem (&bcast_filter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));\r
+\r
+  bcast_filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;\r
+  bcast_filter.IpCnt    = 0;\r
+\r
+  efi_status = Private->PxeBc->SetIpFilter (\r
+                                Private->PxeBc,\r
+                                &bcast_filter\r
+                                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Configure station IP address and subnet mask...\r
+  //\r
+  efi_status = Private->PxeBc->SetStationIp (\r
+                                Private->PxeBc,\r
+                                StationIp,\r
+                                SubnetMask\r
+                                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+  }\r
+\r
+  return efi_status;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+stop_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private\r
+  )\r
+{\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (Private->PxeBc);\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+start_receive_events (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN UINTN                  SecondsTimeout\r
+  )\r
+{\r
+  EFI_STATUS  efi_status;\r
+  UINTN       random;\r
+\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (SecondsTimeout);\r
+\r
+  if (Private == NULL || SecondsTimeout == 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Need a bettern randomizer...\r
+  // For now adjust the timeout value by the least significant\r
+  // digit in the MAC address.\r
+  //\r
+  random = 0;\r
+\r
+  if (Private->PxeDhcp4.Data != NULL) {\r
+    if (Private->PxeDhcp4.Data->Discover.dhcp4.hlen != 0 && Private->PxeDhcp4.Data->Discover.dhcp4.hlen <= 16) {\r
+      random = 0xFFF & Private->PxeDhcp4.Data->Discover.dhcp4.chaddr[Private->PxeDhcp4.Data->Discover.dhcp4.hlen - 1];\r
+    }\r
+  }\r
+  //\r
+  // Setup timeout event and start timer.\r
+  //\r
+  efi_status = gBS->CreateEvent (\r
+                      EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                      TPL_NOTIFY,\r
+                      &timeout_notify,\r
+                      Private,\r
+                      &Private->TimeoutEvent\r
+                      );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    return efi_status;\r
+  }\r
+\r
+  efi_status = gBS->SetTimer (\r
+                      Private->TimeoutEvent,\r
+                      TimerRelative,\r
+                      SecondsTimeout * 10000000 + random\r
+                      );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    gBS->CloseEvent (Private->TimeoutEvent);\r
+    return efi_status;\r
+  }\r
+\r
+  Private->TimeoutOccurred = FALSE;\r
+\r
+  //\r
+  // Setup periodic event for callbacks\r
+  //\r
+  efi_status = gBS->CreateEvent (\r
+                      EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                      TPL_NOTIFY,\r
+                      &periodic_notify,\r
+                      Private,\r
+                      &Private->PeriodicEvent\r
+                      );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    gBS->CloseEvent (Private->TimeoutEvent);\r
+    return efi_status;\r
+  }\r
+\r
+  efi_status = gBS->SetTimer (\r
+                      Private->PeriodicEvent,\r
+                      TimerPeriodic,\r
+                      1000000\r
+                      );  /* 1/10th second */\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    gBS->CloseEvent (Private->TimeoutEvent);\r
+    gBS->CloseEvent (Private->PeriodicEvent);\r
+    return efi_status;\r
+  }\r
+\r
+  Private->PeriodicOccurred = FALSE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+VOID\r
+stop_receive_events (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private\r
+  )\r
+{\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+\r
+  if (Private == NULL) {\r
+    return ;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  gBS->CloseEvent (Private->TimeoutEvent);\r
+  Private->TimeoutOccurred = FALSE;\r
+\r
+  //\r
+  //\r
+  //\r
+  gBS->CloseEvent (Private->PeriodicEvent);\r
+  Private->PeriodicOccurred = FALSE;\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_INVALID_PARAMETER := Private == NULL || dest_ip == NULL ||\r
+  @return buffer == NULL || BufferSize < 300 || Private->PxeBc == NULL\r
+  @return EFI_SUCCESS := Buffer was transmitted\r
+  @return other := Return from PxeBc->UdpWrite()\r
+\r
+**/\r
+EFI_STATUS\r
+tx_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN EFI_IP_ADDRESS         *dest_ip,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *gateway_ip,\r
+  IN EFI_IP_ADDRESS         *src_ip,\r
+  IN VOID                   *buffer,\r
+  IN UINTN                  BufferSize\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT  dest_port;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  src_port;\r
+  EFI_IP_ADDRESS              zero_ip;\r
+\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (dest_ip);\r
+  ASSERT (buffer);\r
+  ASSERT (BufferSize >= 300);\r
+\r
+  if (Private == NULL || dest_ip == NULL || buffer == NULL || BufferSize < 300) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ASSERT (Private->PxeBc);\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Transmit DHCP discover packet...\r
+  //\r
+  ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));\r
+\r
+  if (src_ip == NULL) {\r
+    src_ip = &zero_ip;\r
+  }\r
+\r
+  dest_port = DHCP4_SERVER_PORT;\r
+  src_port  = DHCP4_CLIENT_PORT;\r
+\r
+  return Private->PxeBc->UdpWrite (\r
+                          Private->PxeBc,\r
+                          EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
+                          dest_ip,\r
+                          &dest_port,\r
+                          gateway_ip,\r
+                          src_ip,\r
+                          &src_port,\r
+                          NULL,\r
+                          NULL,\r
+                          &BufferSize,\r
+                          buffer\r
+                          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+\r
+/**\r
+\r
+  @return EFI_INVALID_PARAMETER :=\r
+  @return EFI_SUCCESS := Packet received\r
+  @return other := Return from PxeBc->UdpRead()\r
+\r
+**/\r
+EFI_STATUS\r
+rx_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  OUT VOID                  *buffer,\r
+  IN OUT UINTN              *BufferSize,\r
+  IN OUT EFI_IP_ADDRESS     *dest_ip,\r
+  IN OUT EFI_IP_ADDRESS     *src_ip,\r
+  IN UINT16                 op_flags\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT  dest_port;\r
+  EFI_PXE_BASE_CODE_UDP_PORT  src_port;\r
+\r
+  //\r
+  //\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (buffer);\r
+  ASSERT (dest_ip);\r
+  ASSERT (src_ip);\r
+\r
+  if (Private == NULL || buffer == NULL || dest_ip == NULL || src_ip == NULL || BufferSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ASSERT (Private->PxeBc);\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Check for packet\r
+  //\r
+  *BufferSize = sizeof (DHCP4_PACKET);\r
+\r
+  dest_port   = DHCP4_CLIENT_PORT;\r
+  src_port    = DHCP4_SERVER_PORT;\r
+\r
+  return Private->PxeBc->UdpRead (\r
+                          Private->PxeBc,\r
+                          op_flags,\r
+                          dest_ip,\r
+                          &dest_port,\r
+                          src_ip,\r
+                          &src_port,\r
+                          NULL,\r
+                          NULL,\r
+                          BufferSize,\r
+                          buffer\r
+                          );\r
+}\r
+\r
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
+EFI_STATUS\r
+tx_rx_udp (\r
+  IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+  IN OUT EFI_IP_ADDRESS     *ServerIp,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *gateway_ip,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *client_ip,\r
+  IN OPTIONAL EFI_IP_ADDRESS         *SubnetMask,\r
+  IN DHCP4_PACKET           *tx_pkt,\r
+  OUT DHCP4_PACKET          *rx_pkt,\r
+  IN INTN (*rx_vfy)(\r
+      IN PXE_DHCP4_PRIVATE_DATA *Private,\r
+      IN DHCP4_PACKET *tx_pkt,\r
+      IN DHCP4_PACKET *rx_pkt,\r
+      IN UINTN rx_pkt_size\r
+    ),\r
+  IN UINTN SecondsTimeout\r
+  )\r
+/*++\r
+Routine description:\r
+  Transmit DHCP packet and wait for replies.\r
+\r
+Parameters:\r
+  Private := Pointer to PxeDhcp4 private data\r
+  ServerIp := Pointer to server IP address\r
+  gateway_ip := Pointer to gateway IP address or NULL\r
+  client_ip := Pointer to client IP address or NULL\r
+  SubnetMask := Pointer to subnet mask or NULL\r
+  tx_pkt := Pointer to DHCP packet to transmit\r
+  rx_pkt := Pointer to DHCP packet receive buffer\r
+  rx_vfy := Pointer to DHCP packet receive verification routine\r
+  SecondsTimeout := Number of seconds until timeout\r
+\r
+Returns:\r
+  EFI_INVALID_PARAMETER := Private == NULL || ServerIp == NULL ||\r
+    tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL || Private->PxeBc == NULL\r
+  EFI_ABORTED := Receive aborted\r
+  EFI_TIMEOUT := No packets received\r
+  EFI_SUCCESS := Packet(s) received\r
+  other := Returns from other PxeDhcp4 support routines\r
+--*/\r
+{\r
+  EFI_PXE_DHCP4_CALLBACK_STATUS CallbackStatus;\r
+  EFI_IP_ADDRESS                dest_ip;\r
+  EFI_IP_ADDRESS                src_ip;\r
+  EFI_STATUS                    efi_status;\r
+  DHCP4_OP                      *msg_size_op;\r
+  UINTN                         pkt_size;\r
+  UINTN                         n;\r
+  UINT16                        msg_size;\r
+  UINT16                        op_flags;\r
+  BOOLEAN                       done_flag;\r
+  BOOLEAN                       got_packet;\r
+\r
+  //\r
+  // Bad programmer check...\r
+  //\r
+  ASSERT (Private);\r
+  ASSERT (ServerIp);\r
+  ASSERT (tx_pkt);\r
+  ASSERT (rx_pkt);\r
+  ASSERT (rx_vfy);\r
+\r
+  if (Private == NULL || ServerIp == NULL || tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ASSERT (Private->PxeBc);\r
+\r
+  if (Private->PxeBc == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Enable UDP...\r
+  //\r
+  efi_status = start_udp (Private, client_ip, SubnetMask);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Get length of transmit packet...\r
+  //\r
+  msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE;\r
+\r
+  efi_status = find_opt (\r
+                tx_pkt,\r
+                DHCP4_MAX_MESSAGE_SIZE,\r
+                0,\r
+                &msg_size_op\r
+                );\r
+\r
+  if (!EFI_ERROR (efi_status)) {\r
+    CopyMem (&msg_size, msg_size_op->data, 2);\r
+\r
+    if ((msg_size = htons (msg_size)) < 328) {\r
+      msg_size = 328;\r
+    }\r
+  }\r
+  //\r
+  // Transmit packet...\r
+  //\r
+  efi_status = tx_udp (\r
+                Private,\r
+                ServerIp,\r
+                gateway_ip,\r
+                client_ip,\r
+                tx_pkt,\r
+                msg_size - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE)\r
+                );\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    stop_udp (Private);\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Enable periodic and timeout events...\r
+  //\r
+  efi_status = start_receive_events (Private, SecondsTimeout);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    stop_udp (Private);\r
+    return efi_status;\r
+  }\r
+  //\r
+  // Wait for packet(s)...\r
+  //\r
+#if 0\r
+  if (!client_ip) {\r
+    Aprint ("client_ip == NULL    ");\r
+  } else {\r
+    Aprint (\r
+      "client_ip == %d.%d.%d.%d    ",\r
+      client_ip->v4.Addr[0],\r
+      client_ip->v4.Addr[1],\r
+      client_ip->v4.Addr[2],\r
+      client_ip->v4.Addr[3]\r
+      );\r
+  }\r
+\r
+  if (!ServerIp) {\r
+    Aprint ("ServerIp == NULL\n");\r
+  } else {\r
+    Aprint (\r
+      "ServerIp == %d.%d.%d.%d\n",\r
+      ServerIp->v4.Addr[0],\r
+      ServerIp->v4.Addr[1],\r
+      ServerIp->v4.Addr[2],\r
+      ServerIp->v4.Addr[3]\r
+      );\r
+  }\r
+#endif\r
+\r
+  done_flag   = FALSE;\r
+  got_packet  = FALSE;\r
+\r
+  while (!done_flag) {\r
+    //\r
+    // Check for timeout event...\r
+    //\r
+    if (Private->TimeoutOccurred) {\r
+      efi_status = EFI_SUCCESS;\r
+      break;\r
+    }\r
+    //\r
+    // Check for periodic event...\r
+    //\r
+    if (Private->PeriodicOccurred && Private->callback != NULL) {\r
+      CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE;\r
+\r
+      if (Private->callback->Callback != NULL) {\r
+        CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, 0, NULL);\r
+      }\r
+\r
+      switch (CallbackStatus) {\r
+      case EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE:\r
+        break;\r
+\r
+      case EFI_PXE_DHCP4_CALLBACK_STATUS_ABORT:\r
+      default:\r
+        stop_receive_events (Private);\r
+        stop_udp (Private);\r
+        return EFI_ABORTED;\r
+      }\r
+\r
+      Private->PeriodicOccurred = FALSE;\r
+    }\r
+    //\r
+    // Check for packet...\r
+    //\r
+    if (client_ip == NULL) {\r
+      SetMem (&dest_ip, sizeof (EFI_IP_ADDRESS), 0xFF);\r
+    } else {\r
+      CopyMem (&dest_ip, client_ip, sizeof (EFI_IP_ADDRESS));\r
+    }\r
+\r
+    SetMem (&src_ip, sizeof (EFI_IP_ADDRESS), 0xFF);\r
+\r
+    if (CompareMem (&src_ip, &ServerIp, sizeof (EFI_IP_ADDRESS))) {\r
+      ZeroMem (&src_ip, sizeof (EFI_IP_ADDRESS));\r
+      op_flags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP;\r
+    } else {\r
+      op_flags = 0;\r
+    }\r
+\r
+    efi_status = rx_udp (\r
+                  Private,\r
+                  rx_pkt,\r
+                  &pkt_size,\r
+                  &dest_ip,\r
+                  &src_ip,\r
+                  op_flags\r
+                  );\r
+\r
+    if (efi_status == EFI_TIMEOUT) {\r
+      efi_status = EFI_SUCCESS;\r
+      continue;\r
+    }\r
+\r
+    if (EFI_ERROR (efi_status)) {\r
+      break;\r
+    }\r
+    //\r
+    // Some basic packet sanity checks..\r
+    //\r
+    if (pkt_size < 300) {\r
+      continue;\r
+    }\r
+\r
+    if (rx_pkt->dhcp4.op != BOOTP_REPLY) {\r
+      continue;\r
+    }\r
+\r
+    if (tx_pkt->dhcp4.htype != rx_pkt->dhcp4.htype) {\r
+      continue;\r
+    }\r
+\r
+    if ((n = tx_pkt->dhcp4.hlen) != rx_pkt->dhcp4.hlen) {\r
+      continue;\r
+    }\r
+\r
+    if (CompareMem (&tx_pkt->dhcp4.xid, &rx_pkt->dhcp4.xid, 4)) {\r
+      continue;\r
+    }\r
+\r
+    if (n != 0) {\r
+      if (n >= 16) {\r
+        n = 16;\r
+      }\r
+\r
+      if (CompareMem (tx_pkt->dhcp4.chaddr, rx_pkt->dhcp4.chaddr, n)) {\r
+        continue;\r
+      }\r
+    }\r
+    //\r
+    // Internal callback packet verification...\r
+    //\r
+    switch ((*rx_vfy) (Private, tx_pkt, rx_pkt, pkt_size)) {\r
+    case -2:  /* ignore and stop */\r
+      stop_receive_events (Private);\r
+      stop_udp (Private);\r
+      return EFI_ABORTED;\r
+\r
+    case -1:  /* ignore and wait */\r
+      continue;\r
+\r
+    case 0:   /* accept and wait */\r
+      break;\r
+\r
+    case 1:   /* accept and stop */\r
+      done_flag = TRUE;\r
+      break;\r
+\r
+    default:\r
+      ASSERT (0);\r
+    }\r
+    //\r
+    // External callback packet verification...\r
+    //\r
+    CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE;\r
+\r
+    if (Private->callback != NULL) {\r
+      if (Private->callback->Callback != NULL) {\r
+        CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, (UINT32) pkt_size, rx_pkt);\r
+      }\r
+    }\r
+\r
+    switch (CallbackStatus) {\r
+    case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_CONTINUE:\r
+      continue;\r
+\r
+    case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_ABORT:\r
+      done_flag = TRUE;\r
+      break;\r
+\r
+    case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_ABORT:\r
+      stop_receive_events (Private);\r
+      stop_udp (Private);\r
+      return EFI_ABORTED;\r
+\r
+    case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE:\r
+    default:\r
+      break;\r
+    }\r
+    //\r
+    // We did!  We did get a packet!\r
+    //\r
+    got_packet = TRUE;\r
+  }\r
+  //\r
+  //\r
+  //\r
+  stop_receive_events (Private);\r
+  stop_udp (Private);\r
+\r
+  if (EFI_ERROR (efi_status)) {\r
+    DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));\r
+    return efi_status;\r
+  }\r
+\r
+  if (got_packet) {\r
+    return EFI_SUCCESS;\r
+  } else {\r
+    return EFI_TIMEOUT;\r
+  }\r
+}\r
+\r
+/* eof - support.c */\r
index 624a44c971d87756d8da2fc1b83ae8655d1ba044..90c9f3d563f792de23f8a51326cff984851be688 100644 (file)
@@ -41,20 +41,6 @@ Revision history:
 
 #define FOUR_GIGABYTES  (UINT64) 0x100000000ULL
 
 
 #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
 
 #define SNP_DRIVER_SIGNATURE  EFI_SIGNATURE_32 ('s', 'n', 'd', 's')
 #define MAX_MAP_LENGTH        100
index 0a43f8e60c5f00420854b82a27270cae71d4ac3a..10c9554a0f3e8d55050d9f90f8cb7dd17d989893 100644 (file)
@@ -33,7 +33,7 @@ pxe_start (
   SNP_DRIVER *snp\r
   )\r
 {\r
   SNP_DRIVER *snp\r
   )\r
 {\r
-  PXE_CPB_START_30     *cpb;\r
+  PXE_CPB_START_30  *cpb;\r
   PXE_CPB_START_31  *cpb_31;\r
 \r
   cpb     = snp->cpb;\r
   PXE_CPB_START_31  *cpb_31;\r
 \r
   cpb     = snp->cpb;\r
index ea7d732b847e25f58b14ca131c678c21c641df89..536a01a1d5391a59c376c1205e68a8d5206f6a6a 100644 (file)
@@ -118,11 +118,11 @@ Tcp4GetMode (
 \r
     AccessPoint->UseDefaultAddress  = Tcb->UseDefaultAddr;\r
 \r
 \r
     AccessPoint->UseDefaultAddress  = Tcb->UseDefaultAddr;\r
 \r
-    EFI_IP4 (AccessPoint->StationAddress) = Tcb->LocalEnd.Ip;\r
+    NetCopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
     AccessPoint->SubnetMask         = Tcb->SubnetMask;\r
     AccessPoint->StationPort        = NTOHS (Tcb->LocalEnd.Port);\r
 \r
     AccessPoint->SubnetMask         = Tcb->SubnetMask;\r
     AccessPoint->StationPort        = NTOHS (Tcb->LocalEnd.Port);\r
 \r
-    EFI_IP4 (AccessPoint->RemoteAddress) = Tcb->RemoteEnd.Ip;\r
+    NetCopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
     AccessPoint->RemotePort         = NTOHS (Tcb->RemoteEnd.Port);\r
     AccessPoint->ActiveFlag         = (BOOLEAN) (Tcb->State != TCP_LISTEN);\r
 \r
     AccessPoint->RemotePort         = NTOHS (Tcb->RemoteEnd.Port);\r
     AccessPoint->ActiveFlag         = (BOOLEAN) (Tcb->State != TCP_LISTEN);\r
 \r
@@ -428,11 +428,11 @@ Tcp4ConfigurePcb (
   Tcb->TTL            = CfgData->TimeToLive;\r
   Tcb->TOS            = CfgData->TypeOfService;\r
 \r
   Tcb->TTL            = CfgData->TimeToLive;\r
   Tcb->TOS            = CfgData->TypeOfService;\r
 \r
-  Tcb->LocalEnd.Ip    = EFI_IP4 (CfgData->AccessPoint.StationAddress);\r
+  NetCopyMem (&Tcb->LocalEnd.Ip, &CfgData->AccessPoint.StationAddress, sizeof (IP4_ADDR));\r
   Tcb->LocalEnd.Port  = HTONS (CfgData->AccessPoint.StationPort);\r
   Tcb->SubnetMask     = CfgData->AccessPoint.SubnetMask;\r
 \r
   Tcb->LocalEnd.Port  = HTONS (CfgData->AccessPoint.StationPort);\r
   Tcb->SubnetMask     = CfgData->AccessPoint.SubnetMask;\r
 \r
-  Tcb->RemoteEnd.Ip   = EFI_IP4 (CfgData->AccessPoint.RemoteAddress);\r
+  NetCopyMem (&Tcb->RemoteEnd.Ip, &CfgData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));\r
   Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort);\r
 \r
   Option              = CfgData->ControlOption;\r
   Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort);\r
 \r
   Option              = CfgData->ControlOption;\r
index 002cd61089522776129dbe9b42dc43c5d0219d3d..d7f9fe73f5b3eed9cf7560a247b2810052d07ab8 100644 (file)
@@ -143,7 +143,6 @@ Tcp4DestroyTimer (
   mTcp4Timer.TimerEvent = NULL;\r
 }\r
 \r
   mTcp4Timer.TimerEvent = NULL;\r
 }\r
 \r
-//@MT: EFI_DRIVER_ENTRY_POINT (Tcp4DriverEntryPoint)\r
 \r
 EFI_STATUS\r
 EFIAPI\r
 \r
 EFI_STATUS\r
 EFIAPI\r
index 2f7a49fcb88dff18955c938f1c8ac8c005767836..898d18f611f198ceb7938be105c7f9a80874a903 100644 (file)
@@ -103,8 +103,8 @@ TcpSendIpPacket (
   Override.TimeToLive               = 255;\r
   Override.DoNotFragment            = FALSE;\r
   Override.Protocol                 = EFI_IP_PROTO_TCP;\r
   Override.TimeToLive               = 255;\r
   Override.DoNotFragment            = FALSE;\r
   Override.Protocol                 = EFI_IP_PROTO_TCP;\r
-  EFI_IP4 (Override.GatewayAddress) = 0;\r
-  EFI_IP4 (Override.SourceAddress)  = Src;\r
+  NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  NetCopyMem (&Override.SourceAddress, &Src, sizeof (EFI_IPv4_ADDRESS));\r
 \r
   Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);\r
 \r
 \r
   Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);\r
 \r
index 8f5a997957333dbeef92bbe3ffdf1f649c918042..4d2e80fcadef8bb4510c96e88360b7e69e3c431e 100644 (file)
@@ -144,6 +144,7 @@ Tcp4Configure (
   EFI_TCP4_OPTION  *Option;\r
   SOCKET           *Sock;\r
   EFI_STATUS       Status;\r
   EFI_TCP4_OPTION  *Option;\r
   SOCKET           *Sock;\r
   EFI_STATUS       Status;\r
+  IP4_ADDR         Ip;\r
 \r
   if (NULL == This) {\r
     return EFI_INVALID_PARAMETER;\r
 \r
   if (NULL == This) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -153,26 +154,25 @@ Tcp4Configure (
   // Tcp protocol related parameter check will be conducted here\r
   //\r
   if (NULL != TcpConfigData) {\r
   // Tcp protocol related parameter check will be conducted here\r
   //\r
   if (NULL != TcpConfigData) {\r
-    if ((EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) != 0) &&\r
-      !Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.RemoteAddress), 0)) {\r
+\r
+    NetCopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));\r
+    if ((Ip != 0) && !Ip4IsUnicast (NTOHL (Ip), 0)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (TcpConfigData->AccessPoint.ActiveFlag &&\r
+      (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) {\r
       return EFI_INVALID_PARAMETER;\r
     }\r
 \r
     if (!TcpConfigData->AccessPoint.UseDefaultAddress) {\r
       return EFI_INVALID_PARAMETER;\r
     }\r
 \r
     if (!TcpConfigData->AccessPoint.UseDefaultAddress) {\r
-      if (!Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.StationAddress), 0) ||\r
-          !IP4_IS_VALID_NETMASK (EFI_NTOHL (TcpConfigData->AccessPoint.SubnetMask))\r
-            ) {\r
+\r
+      NetCopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR));\r
+      if (!Ip4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (Ip))) {\r
         return EFI_INVALID_PARAMETER;\r
       }\r
     }\r
 \r
         return EFI_INVALID_PARAMETER;\r
       }\r
     }\r
 \r
-    if (TcpConfigData->AccessPoint.ActiveFlag &&\r
-        (0 == TcpConfigData->AccessPoint.RemotePort ||\r
-        (EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) == 0))\r
-        ) {\r
-      return EFI_INVALID_PARAMETER;\r
-    }\r
-\r
     Option = TcpConfigData->ControlOption;\r
     if ((NULL != Option) &&\r
         (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {\r
     Option = TcpConfigData->ControlOption;\r
     if ((NULL != Option) &&\r
         (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {\r
index dba46aa80adc190ccb241ee77f6f6bab0bc816d0..3e6575be92e4b4b000981c5bc0600ba31a6cece1 100644 (file)
@@ -32,7 +32,6 @@ extern UINT16  mTcp4RandomPort;
 //
 // Driver Produced Protocol Prototypes
 //
 //
 // 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)
 
 #define TCP4_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR   ("Tcp", PrintArg)
 #define TCP4_DEBUG_WARN(PrintArg)  NET_DEBUG_WARNING ("Tcp", PrintArg)
index 9cb55a567d655aab8176579f05db1b65f47d77ed..1cc84881118e2c043de0595deb52d80bafb7931d 100644 (file)
@@ -267,7 +267,7 @@ TcpFindTcbByPeer (
   NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
     Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
 \r
   NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
     Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
 \r
-    if ((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip) &&\r
+    if (EFI_IP4_EQUAL (*Addr, Tcb->LocalEnd.Ip) &&\r
       (LocalPort == Tcb->LocalEnd.Port)) {\r
 \r
       return TRUE;\r
       (LocalPort == Tcb->LocalEnd.Port)) {\r
 \r
       return TRUE;\r
@@ -277,7 +277,7 @@ TcpFindTcbByPeer (
   NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
     Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
 \r
   NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
     Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
 \r
-    if (((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip)) &&\r
+    if (EFI_IP4_EQUAL (*Addr, Tcb->LocalEnd.Ip) &&\r
       (LocalPort == Tcb->LocalEnd.Port)) {\r
 \r
       return TRUE;\r
       (LocalPort == Tcb->LocalEnd.Port)) {\r
 \r
       return TRUE;\r
@@ -983,9 +983,9 @@ TcpSetVariableData (
       // This tcp instance belongs to the Tcp4Service.\r
       //\r
       Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
       // This tcp instance belongs to the Tcp4Service.\r
       //\r
       Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
-      EFI_IP4 (Tcp4ServicePoint->LocalAddress)  = TcpPcb->LocalEnd.Ip;\r
+      NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
       Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
       Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
-      EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip;\r
+      NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
       Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
 \r
       Tcp4ServicePoint++;\r
       Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
 \r
       Tcp4ServicePoint++;\r
@@ -1005,9 +1005,9 @@ TcpSetVariableData (
       // This tcp instance belongs to the Tcp4Service.\r
       //\r
       Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
       // This tcp instance belongs to the Tcp4Service.\r
       //\r
       Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
-      EFI_IP4 (Tcp4ServicePoint->LocalAddress)  = TcpPcb->LocalEnd.Ip;\r
+      NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
       Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
       Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
-      EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip;\r
+      NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
       Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
 \r
       Tcp4ServicePoint++;\r
       Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
 \r
       Tcp4ServicePoint++;\r
index 7b47ef44a92a8bf84c6b8daa530114c0c62e30f7..ada73d79db7249bedc2d1f73d7ab66c8b5e43189 100644 (file)
@@ -473,7 +473,6 @@ Udp4ServiceBindingDestroyChild (
   return EFI_SUCCESS;\r
 }\r
 \r
   return EFI_SUCCESS;\r
 }\r
 \r
-//@MT: EFI_DRIVER_ENTRY_POINT (Udp4DriverEntryPoint)\r
 \r
 EFI_STATUS\r
 EFIAPI\r
 \r
 EFI_STATUS\r
 EFIAPI\r
index e6fddd80520498f0f39a39fcb981555c674d014d..e5576820a66a436811a4822c74d47d9c2e2f0a73 100644 (file)
@@ -424,7 +424,7 @@ Udp4FindInstanceByPort (
       continue;\r
     }\r
 \r
       continue;\r
     }\r
 \r
-    if (EFI_IP_EQUAL (ConfigData->StationAddress, *Address) &&\r
+    if (EFI_IP4_EQUAL (ConfigData->StationAddress, *Address) &&\r
       (ConfigData->StationPort == Port)) {\r
       //\r
       // if both the address and the port are the same, return TRUE.\r
       (ConfigData->StationPort == Port)) {\r
       //\r
       // if both the address and the port are the same, return TRUE.\r
@@ -566,8 +566,8 @@ Udp4IsReconfigurable (
     }\r
 \r
     if (!NewConfigData->UseDefaultAddress &&\r
     }\r
 \r
     if (!NewConfigData->UseDefaultAddress &&\r
-      (!EFI_IP_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) ||\r
-      !EFI_IP_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) {\r
+      (!EFI_IP4_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) ||\r
+      !EFI_IP4_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) {\r
       //\r
       // If the instance doesn't use the default address, and the new address or\r
       // new subnet mask is different from the old values.\r
       //\r
       // If the instance doesn't use the default address, and the new address or\r
       // new subnet mask is different from the old values.\r
@@ -576,15 +576,14 @@ Udp4IsReconfigurable (
     }\r
   }\r
 \r
     }\r
   }\r
 \r
-  if (!EFI_IP_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) {\r
+  if (!EFI_IP4_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) {\r
     //\r
     // The remoteaddress is not the same.\r
     //\r
     return FALSE;\r
   }\r
 \r
     //\r
     // The remoteaddress is not the same.\r
     //\r
     return FALSE;\r
   }\r
 \r
-  if ((EFI_IP4 (NewConfigData->RemoteAddress) != 0) &&\r
-    (NewConfigData->RemotePort != OldConfigData->RemotePort)) {\r
+  if (!EFI_IP4_EQUAL (NewConfigData->RemoteAddress, mZeroIp4Addr) && (NewConfigData->RemotePort != OldConfigData->RemotePort)) {\r
     //\r
     // The RemotePort differs if it's designated in the configdata.\r
     //\r
     //\r
     // The RemotePort differs if it's designated in the configdata.\r
     //\r
@@ -667,6 +666,7 @@ Udp4ValidateTxToken (
   EFI_UDP4_CONFIG_DATA    *ConfigData;\r
   EFI_UDP4_SESSION_DATA   *UdpSessionData;\r
   IP4_ADDR                SourceAddress;\r
   EFI_UDP4_CONFIG_DATA    *ConfigData;\r
   EFI_UDP4_SESSION_DATA   *UdpSessionData;\r
   IP4_ADDR                SourceAddress;\r
+  IP4_ADDR                GatewayAddress;\r
 \r
   if (TxToken->Event == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
 \r
   if (TxToken->Event == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -700,12 +700,15 @@ Udp4ValidateTxToken (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  if ((TxData->GatewayAddress != NULL) &&\r
-    !Ip4IsUnicast(EFI_NTOHL (*(TxData->GatewayAddress)), 0)) {\r
-    //\r
-    // The specified GatewayAddress is not a unicast IPv4 address while it's not 0.\r
-    //\r
-    return EFI_INVALID_PARAMETER;\r
+  if (TxData->GatewayAddress != NULL) {\r
+    NetCopyMem (&GatewayAddress, TxData->GatewayAddress, sizeof (IP4_ADDR));\r
+\r
+    if (!Ip4IsUnicast (NTOHL (GatewayAddress), 0)) {\r
+      //\r
+      // The specified GatewayAddress is not a unicast IPv4 address while it's not 0.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
   }\r
 \r
   ConfigData     = &Instance->ConfigData;\r
   }\r
 \r
   ConfigData     = &Instance->ConfigData;\r
@@ -713,9 +716,9 @@ Udp4ValidateTxToken (
 \r
   if (UdpSessionData != NULL) {\r
 \r
 \r
   if (UdpSessionData != NULL) {\r
 \r
-    SourceAddress = EFI_NTOHL (UdpSessionData->SourceAddress);\r
+    NetCopyMem (&SourceAddress, &UdpSessionData->SourceAddress, sizeof (IP4_ADDR));\r
 \r
 \r
-    if ((SourceAddress != 0) && !Ip4IsUnicast (SourceAddress, 0)) {\r
+    if ((SourceAddress != 0) && !Ip4IsUnicast (HTONL (SourceAddress), 0)) {\r
       //\r
       // Check whether SourceAddress is a valid IPv4 address in case it's not zero.\r
       // The configured station address is used if SourceAddress is zero.\r
       //\r
       // Check whether SourceAddress is a valid IPv4 address in case it's not zero.\r
       // The configured station address is used if SourceAddress is zero.\r
@@ -730,13 +733,13 @@ Udp4ValidateTxToken (
       return EFI_INVALID_PARAMETER;\r
     }\r
 \r
       return EFI_INVALID_PARAMETER;\r
     }\r
 \r
-    if (EFI_IP4 (UdpSessionData->DestinationAddress) == 0) {\r
+    if (EFI_IP4_EQUAL (UdpSessionData->DestinationAddress, mZeroIp4Addr)) {\r
       //\r
       // The DestinationAddress specified in the UdpSessionData is 0.\r
       //\r
       return EFI_INVALID_PARAMETER;\r
     }\r
       //\r
       // The DestinationAddress specified in the UdpSessionData is 0.\r
       //\r
       return EFI_INVALID_PARAMETER;\r
     }\r
-  } else if (EFI_IP4 (ConfigData->RemoteAddress) == 0) {\r
+  } else if (EFI_IP4_EQUAL (ConfigData->RemoteAddress, mZeroIp4Addr)) {\r
     //\r
     // the configured RemoteAddress is all zero, and the user doens't override the\r
     // destination address.\r
     //\r
     // the configured RemoteAddress is all zero, and the user doens't override the\r
     // destination address.\r
@@ -959,9 +962,9 @@ Udp4LeaveGroup (
 \r
   McastIp = Arg;\r
 \r
 \r
   McastIp = Arg;\r
 \r
-  if ((McastIp != NULL) && ((UINTN) EFI_IP4 (*McastIp) != (UINTN) (Item->Key))) {\r
+  if ((McastIp != NULL) && (!EFI_IP4_EQUAL (*McastIp, (UINTN) Item->Key))) {\r
     //\r
     //\r
-    // McastIp is not NULL and the multicast address contained in the Item\r
+    // McastIp is not NULL and the multicast address contained in the Item \r
     // is not the same as McastIp.\r
     //\r
     return EFI_SUCCESS;\r
     // is not the same as McastIp.\r
     //\r
     return EFI_SUCCESS;\r
@@ -1169,16 +1172,16 @@ Udp4MatchDgram (
     return FALSE;\r
   }\r
 \r
     return FALSE;\r
   }\r
 \r
-  if ((EFI_IP4 (ConfigData->RemoteAddress) != 0) &&\r
-    !EFI_IP_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) {\r
+  if (!EFI_IP4_EQUAL (ConfigData->RemoteAddress, mZeroIp4Addr) &&\r
+    !EFI_IP4_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) {\r
     //\r
     // This datagram doesn't come from the instance's specified sender.\r
     //\r
     return FALSE;\r
   }\r
 \r
     //\r
     // This datagram doesn't come from the instance's specified sender.\r
     //\r
     return FALSE;\r
   }\r
 \r
-  if ((EFI_IP4 (ConfigData->StationAddress) == 0) ||\r
-    EFI_IP_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) {\r
+  if (EFI_IP4_EQUAL (ConfigData->StationAddress, mZeroIp4Addr) ||\r
+    EFI_IP4_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) {\r
     //\r
     // The instance is configured to receive datagrams destinated to any station IP or\r
     // the destination address of this datagram matches the configured station IP.\r
     //\r
     // The instance is configured to receive datagrams destinated to any station IP or\r
     // the destination address of this datagram matches the configured station IP.\r
@@ -1186,7 +1189,7 @@ Udp4MatchDgram (
     return TRUE;\r
   }\r
 \r
     return TRUE;\r
   }\r
 \r
-  Destination = EFI_IP4 (Udp4Session->DestinationAddress);\r
+  NetCopyMem (&Destination, &Udp4Session->DestinationAddress, sizeof (IP4_ADDR));\r
 \r
   if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) {\r
     //\r
 \r
   if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) {\r
     //\r
@@ -1513,11 +1516,12 @@ Udp4Demultiplex (
 \r
   gRT->GetTime (&RxData.TimeStamp, NULL);\r
 \r
 \r
   gRT->GetTime (&RxData.TimeStamp, NULL);\r
 \r
-  Udp4Session                               = &RxData.UdpSession;\r
-  EFI_IP4 (Udp4Session->SourceAddress)      = NetSession->Source;\r
-  EFI_IP4 (Udp4Session->DestinationAddress) = NetSession->Dest;\r
-  Udp4Session->SourcePort                   = NTOHS (Udp4Header->SrcPort);\r
-  Udp4Session->DestinationPort              = NTOHS (Udp4Header->DstPort);\r
+  Udp4Session                  = &RxData.UdpSession;\r
+  Udp4Session->SourcePort      = NTOHS (Udp4Header->SrcPort);\r
+  Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort);\r
+\r
+  NetCopyMem (&Udp4Session->SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS));\r
+  NetCopyMem (&Udp4Session->DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));\r
 \r
   //\r
   // Trim the UDP header.\r
 \r
   //\r
   // Trim the UDP header.\r
@@ -1637,12 +1641,13 @@ Udp4SendPortUnreach (
   //\r
   // Fill the override data.\r
   //\r
   //\r
   // Fill the override data.\r
   //\r
-  Override.DoNotFragment            = FALSE;\r
-  Override.TypeOfService            = 0;\r
-  Override.TimeToLive               = 255;\r
-  Override.Protocol                 = EFI_IP_PROTO_ICMP;\r
-  EFI_IP4 (Override.SourceAddress)  = NetSession->Dest;\r
-  EFI_IP4 (Override.GatewayAddress) = 0;\r
+  Override.DoNotFragment = FALSE;\r
+  Override.TypeOfService = 0;\r
+  Override.TimeToLive    = 255;\r
+  Override.Protocol      = EFI_IP_PROTO_ICMP;\r
+\r
+  NetCopyMem (&Override.SourceAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));\r
+  NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));\r
 \r
   //\r
   // Send out this icmp packet.\r
 \r
   //\r
   // Send out this icmp packet.\r
@@ -1682,10 +1687,11 @@ Udp4IcmpHandler (
 \r
   Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
 \r
 \r
   Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
 \r
-  EFI_IP4 (Udp4Session.SourceAddress)      = NetSession->Source;\r
-  EFI_IP4 (Udp4Session.DestinationAddress) = NetSession->Dest;\r
-  Udp4Session.SourcePort                   = NTOHS (Udp4Header->DstPort);\r
-  Udp4Session.DestinationPort              = NTOHS (Udp4Header->SrcPort);\r
+  NetCopyMem (&Udp4Session.SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS));\r
+  NetCopyMem (&Udp4Session.DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Udp4Session.SourcePort      = NTOHS (Udp4Header->DstPort);\r
+  Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort);\r
 \r
   NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {\r
     //\r
 \r
   NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {\r
     //\r
@@ -1696,7 +1702,7 @@ Udp4IcmpHandler (
     if (!Instance->Configured ||\r
       Instance->ConfigData.AcceptPromiscuous ||\r
       Instance->ConfigData.AcceptAnyPort ||\r
     if (!Instance->Configured ||\r
       Instance->ConfigData.AcceptPromiscuous ||\r
       Instance->ConfigData.AcceptAnyPort ||\r
-      (EFI_IP4 (Instance->ConfigData.StationAddress) == 0)) {\r
+      EFI_IP4_EQUAL (Instance->ConfigData.StationAddress, mZeroIp4Addr)) {\r
       //\r
       // Don't try to deliver the ICMP error to this instance if it is not configured,\r
       // or it's configured to be promiscuous or accept any port or accept all the\r
       //\r
       // Don't try to deliver the ICMP error to this instance if it is not configured,\r
       // or it's configured to be promiscuous or accept any port or accept all the\r
index 5343bd5c4bedd152df1fedabb4d7eb67f7d848ed..b3daacba231d80d18f29a31dafecdfcdd77fc37f 100644 (file)
@@ -153,6 +153,8 @@ Udp4Configure (
   IP4_ADDR             SubnetMask;\r
   IP4_ADDR             RemoteAddress;\r
   EFI_IP4_CONFIG_DATA  Ip4ConfigData;\r
   IP4_ADDR             SubnetMask;\r
   IP4_ADDR             RemoteAddress;\r
   EFI_IP4_CONFIG_DATA  Ip4ConfigData;\r
+  IP4_ADDR             LocalAddr;\r
+  IP4_ADDR             RemoteAddr;\r
 \r
   if (This == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
 \r
   if (This == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -171,9 +173,14 @@ Udp4Configure (
 \r
   if (UdpConfigData != NULL) {\r
 \r
 \r
   if (UdpConfigData != NULL) {\r
 \r
-    StationAddress = EFI_NTOHL (UdpConfigData->StationAddress);\r
-    SubnetMask     = EFI_NTOHL (UdpConfigData->SubnetMask);\r
-    RemoteAddress  = EFI_NTOHL (UdpConfigData->RemoteAddress);\r
+    NetCopyMem (&StationAddress, &UdpConfigData->StationAddress, sizeof (IP4_ADDR));\r
+    NetCopyMem (&SubnetMask, &UdpConfigData->SubnetMask, sizeof (IP4_ADDR));\r
+    NetCopyMem (&RemoteAddress, &UdpConfigData->RemoteAddress, sizeof (IP4_ADDR));\r
+\r
+    StationAddress = NTOHL (StationAddress);\r
+    SubnetMask     = NTOHL (SubnetMask);\r
+    RemoteAddress  = NTOHL (RemoteAddress);\r
+    \r
 \r
     if (!UdpConfigData->UseDefaultAddress &&\r
       (!IP4_IS_VALID_NETMASK (SubnetMask) ||\r
 \r
     if (!UdpConfigData->UseDefaultAddress &&\r
       (!IP4_IS_VALID_NETMASK (SubnetMask) ||\r
@@ -251,9 +258,11 @@ Udp4Configure (
       //\r
       // Pre calculate the checksum for the pseudo head, ignore the UDP length first.\r
       //\r
       //\r
       // Pre calculate the checksum for the pseudo head, ignore the UDP length first.\r
       //\r
+      NetCopyMem (&LocalAddr, &Instance->ConfigData.StationAddress, sizeof (IP4_ADDR));\r
+      NetCopyMem (&RemoteAddr, &Instance->ConfigData.RemoteAddress, sizeof (IP4_ADDR));\r
       Instance->HeadSum = NetPseudoHeadChecksum (\r
       Instance->HeadSum = NetPseudoHeadChecksum (\r
-                            EFI_IP4 (Instance->ConfigData.StationAddress),\r
-                            EFI_IP4 (Instance->ConfigData.RemoteAddress),\r
+                            LocalAddr,\r
+                            RemoteAddr,\r
                             EFI_IP_PROTO_UDP,\r
                             0\r
                             );\r
                             EFI_IP_PROTO_UDP,\r
                             0\r
                             );\r
@@ -332,13 +341,21 @@ Udp4Groups (
   UDP4_INSTANCE_DATA  *Instance;\r
   EFI_IP4_PROTOCOL    *Ip;\r
   EFI_TPL             OldTpl;\r
   UDP4_INSTANCE_DATA  *Instance;\r
   EFI_IP4_PROTOCOL    *Ip;\r
   EFI_TPL             OldTpl;\r
+  IP4_ADDR            McastIp;\r
 \r
 \r
-  if ((This == NULL) ||\r
-    (JoinFlag && (MulticastAddress == NULL)) ||\r
-    (JoinFlag && !IP4_IS_MULTICAST (EFI_NTOHL (*MulticastAddress)))) {\r
+  if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
+  McastIp = 0;\r
+  if (JoinFlag) {\r
+    NetCopyMem (&McastIp, MulticastAddress, sizeof (IP4_ADDR));\r
+\r
+    if (IP4_IS_MULTICAST (NTOHL (McastIp))) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
   Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);\r
 \r
   if (Instance->IsNoMapping) {\r
   Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);\r
 \r
   if (Instance->IsNoMapping) {\r
@@ -370,11 +387,7 @@ Udp4Groups (
   //\r
   if (JoinFlag) {\r
 \r
   //\r
   if (JoinFlag) {\r
 \r
-    NetMapInsertTail (\r
-      &Instance->McastIps,\r
-      (VOID *) (UINTN) EFI_IP4 (*MulticastAddress),\r
-      NULL\r
-      );\r
+    NetMapInsertTail (&Instance->McastIps, (VOID *) (UINTN) McastIp, NULL);\r
   } else {\r
 \r
     NetMapIterate (&Instance->McastIps, Udp4LeaveGroup, MulticastAddress);\r
   } else {\r
 \r
     NetMapIterate (&Instance->McastIps, Udp4LeaveGroup, MulticastAddress);\r
@@ -506,6 +519,7 @@ Udp4Transmit (
   NET_BUF                 *Packet;\r
   EFI_UDP4_HEADER         *Udp4Header;\r
   EFI_UDP4_CONFIG_DATA    *ConfigData;\r
   NET_BUF                 *Packet;\r
   EFI_UDP4_HEADER         *Udp4Header;\r
   EFI_UDP4_CONFIG_DATA    *ConfigData;\r
+  IP4_ADDR                Source;\r
   IP4_ADDR                Destination;\r
   EFI_UDP4_TRANSMIT_DATA  *TxData;\r
   EFI_UDP4_SESSION_DATA   *UdpSessionData;\r
   IP4_ADDR                Destination;\r
   EFI_UDP4_TRANSMIT_DATA  *TxData;\r
   EFI_UDP4_SESSION_DATA   *UdpSessionData;\r
@@ -590,25 +604,26 @@ Udp4Transmit (
     // Set the SourceAddress, SrcPort and Destination according to the specified\r
     // UdpSessionData.\r
     //\r
     // Set the SourceAddress, SrcPort and Destination according to the specified\r
     // UdpSessionData.\r
     //\r
-    if (EFI_IP4 (UdpSessionData->SourceAddress) != 0) {\r
-      Override.SourceAddress = UdpSessionData->SourceAddress;\r
+    if (!EFI_IP4_EQUAL (UdpSessionData->SourceAddress, mZeroIp4Addr)) {\r
+      NetCopyMem (&Override.SourceAddress, &UdpSessionData->SourceAddress, sizeof (EFI_IPv4_ADDRESS));\r
     }\r
 \r
     if (UdpSessionData->SourcePort != 0) {\r
       Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort);\r
     }\r
 \r
     }\r
 \r
     if (UdpSessionData->SourcePort != 0) {\r
       Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort);\r
     }\r
 \r
-    Destination = EFI_IP4 (UdpSessionData->DestinationAddress);\r
-\r
     if (UdpSessionData->DestinationPort != 0) {\r
       Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort);\r
     }\r
 \r
     if (UdpSessionData->DestinationPort != 0) {\r
       Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort);\r
     }\r
 \r
+    NetCopyMem (&Source, &Override.SourceAddress, sizeof (IP4_ADDR));\r
+    NetCopyMem (&Destination, &UdpSessionData->DestinationAddress, sizeof (IP4_ADDR));\r
+\r
     //\r
     // calculate the pseudo head checksum using the overridden parameters.\r
     //\r
     HeadSum = NetPseudoHeadChecksum (\r
     //\r
     // calculate the pseudo head checksum using the overridden parameters.\r
     //\r
     HeadSum = NetPseudoHeadChecksum (\r
-                EFI_IP4 (Override.SourceAddress),\r
+                Source,\r
                 Destination,\r
                 EFI_IP_PROTO_UDP,\r
                 0\r
                 Destination,\r
                 EFI_IP_PROTO_UDP,\r
                 0\r
@@ -617,8 +632,9 @@ Udp4Transmit (
     //\r
     // UdpSessionData is NULL, use the address and port information previously configured.\r
     //\r
     //\r
     // UdpSessionData is NULL, use the address and port information previously configured.\r
     //\r
-    Destination = EFI_IP4 (ConfigData->RemoteAddress);\r
-    HeadSum     = Instance->HeadSum;\r
+    NetCopyMem (&Destination, &ConfigData->RemoteAddress, sizeof (IP4_ADDR));\r
+\r
+    HeadSum = Instance->HeadSum;\r
   }\r
 \r
   //\r
   }\r
 \r
   //\r
@@ -635,8 +651,12 @@ Udp4Transmit (
   //\r
   // Fill the IpIo Override data.\r
   //\r
   //\r
   // Fill the IpIo Override data.\r
   //\r
-  EFI_IP4 (Override.GatewayAddress) = (TxData->GatewayAddress != NULL) ?\r
-                                      EFI_IP4 (*(TxData->GatewayAddress)) : 0;\r
+  if (TxData->GatewayAddress != NULL) {\r
+    NetCopyMem (&Override.GatewayAddress, TxData->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  } else {\r
+    NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  }\r
+\r
   Override.Protocol                 = EFI_IP_PROTO_UDP;\r
   Override.TypeOfService            = ConfigData->TypeOfService;\r
   Override.TimeToLive               = ConfigData->TimeToLive;\r
   Override.Protocol                 = EFI_IP_PROTO_UDP;\r
   Override.TypeOfService            = ConfigData->TypeOfService;\r
   Override.TimeToLive               = ConfigData->TimeToLive;\r