summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe')
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/ComponentName.c431
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Driver.c732
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Driver.h146
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Dxe.inf66
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Dxe.uni18
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4DxeExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Impl.c1802
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Impl.h206
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Io.c1657
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Io.h189
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Option.c890
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Option.h228
12 files changed, 6379 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/ComponentName.c
new file mode 100644
index 00000000..d6057962
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/ComponentName.c
@@ -0,0 +1,431 @@
+/** @file
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+DhcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+DhcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDhcp4ComponentName = {
+ DhcpComponentNameGetDriverName,
+ DhcpComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDhcp4ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) DhcpComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) DhcpComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDhcpDriverNameTable[] = {
+ {
+ "eng;en",
+ L"DHCP Protocol Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gDhcpControllerNameTable = NULL;
+
+CHAR16 *mDhcp4ControllerName[] = {
+ L"DHCPv4 (State=0, Stopped)",
+ L"DHCPv4 (State=1, Init)",
+ L"DHCPv4 (State=2, Selecting)",
+ L"DHCPv4 (State=3, Requesting)",
+ L"DHCPv4 (State=4, Bound)",
+ L"DHCPv4 (State=5, Renewing)",
+ L"DHCPv4 (State=6, Rebinding)",
+ L"DHCPv4 (State=7, InitReboot)",
+ L"DHCPv4 (State=8, Rebooting)"
+};
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+DhcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDhcpDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gDhcp4ComponentName)
+ );
+}
+
+/**
+ Update the component name for the Dhcp4 child handle.
+
+ @param Dhcp4[in] A pointer to the EFI_DHCP4_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval EFI_DEVICE_ERROR DHCP is in unknown state.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_DHCP4_PROTOCOL *Dhcp4
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP4_MODE_DATA Dhcp4ModeData;
+
+ if (Dhcp4 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer.
+ //
+ Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (gDhcpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gDhcpControllerNameTable);
+ gDhcpControllerNameTable = NULL;
+ }
+
+ if (Dhcp4ModeData.State > Dhcp4Rebooting) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gDhcp4ComponentName.SupportedLanguages,
+ &gDhcpControllerNameTable,
+ mDhcp4ControllerName[Dhcp4ModeData.State],
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gDhcp4ComponentName2.SupportedLanguages,
+ &gDhcpControllerNameTable,
+ mDhcp4ControllerName[Dhcp4ModeData.State],
+ FALSE
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+DhcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **)&Dhcp4,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Dhcp4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gDhcpControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gDhcp4ComponentName)
+ );
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Driver.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Driver.c
new file mode 100644
index 00000000..d8393b59
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Driver.c
@@ -0,0 +1,732 @@
+/** @file
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Dhcp4Impl.h"
+#include "Dhcp4Driver.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gDhcp4DriverBinding = {
+ Dhcp4DriverBindingSupported,
+ Dhcp4DriverBindingStart,
+ Dhcp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL mDhcp4ServiceBindingTemplate = {
+ Dhcp4ServiceBindingCreateChild,
+ Dhcp4ServiceBindingDestroyChild
+};
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ Entry point of the DHCP driver to install various protocols.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDhcp4DriverBinding,
+ ImageHandle,
+ &gDhcp4ComponentName,
+ &gDhcp4ComponentName2
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device
+ @retval EFI_ALREADY_STARTED This driver is already running on this device
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Configure the default UDP child to receive all the DHCP traffics
+ on this network interface.
+
+ @param[in] UdpIo The UDP IO to configure
+ @param[in] Context The context to the function
+
+ @retval EFI_SUCCESS The UDP IO is successfully configured.
+ @retval Others Failed to configure the UDP child.
+
+**/
+EFI_STATUS
+EFIAPI
+DhcpConfigUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ EFI_UDP4_CONFIG_DATA UdpConfigData;
+
+ UdpConfigData.AcceptBroadcast = TRUE;
+ UdpConfigData.AcceptPromiscuous = FALSE;
+ UdpConfigData.AcceptAnyPort = FALSE;
+ UdpConfigData.AllowDuplicatePort = TRUE;
+ UdpConfigData.TypeOfService = 0;
+ UdpConfigData.TimeToLive = 64;
+ UdpConfigData.DoNotFragment = FALSE;
+ UdpConfigData.ReceiveTimeout = 0;
+ UdpConfigData.TransmitTimeout = 0;
+
+ UdpConfigData.UseDefaultAddress = FALSE;
+ UdpConfigData.StationPort = DHCP_CLIENT_PORT;
+ UdpConfigData.RemotePort = DHCP_SERVER_PORT;
+
+ ZeroMem (&UdpConfigData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
+ ZeroMem (&UdpConfigData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);;
+}
+
+
+
+/**
+ Destroy the DHCP service. The Dhcp4 service may be partly initialized,
+ or partly destroyed. If a resource is destroyed, it is marked as so in
+ case the destroy failed and being called again later.
+
+ @param[in] DhcpSb The DHCP service instance to destroy.
+
+ @retval EFI_SUCCESS Always return success.
+
+**/
+EFI_STATUS
+Dhcp4CloseService (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ DhcpCleanLease (DhcpSb);
+
+ if (DhcpSb->UdpIo != NULL) {
+ UdpIoFreeIo (DhcpSb->UdpIo);
+ DhcpSb->UdpIo = NULL;
+ }
+
+ if (DhcpSb->Timer != NULL) {
+ gBS->SetTimer (DhcpSb->Timer, TimerCancel, 0);
+ gBS->CloseEvent (DhcpSb->Timer);
+
+ DhcpSb->Timer = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Create a new DHCP service binding instance for the controller.
+
+ @param[in] Controller The controller to install DHCP service binding
+ protocol onto
+ @param[in] ImageHandle The driver's image handle
+ @param[out] Service The variable to receive the created DHCP service
+ instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource .
+ @retval EFI_SUCCESS The DHCP service instance is created.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Dhcp4CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ OUT DHCP_SERVICE **Service
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+
+ *Service = NULL;
+ DhcpSb = AllocateZeroPool (sizeof (DHCP_SERVICE));
+
+ if (DhcpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DhcpSb->Signature = DHCP_SERVICE_SIGNATURE;
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+ DhcpSb->Controller = Controller;
+ DhcpSb->Image = ImageHandle;
+ InitializeListHead (&DhcpSb->Children);
+ DhcpSb->DhcpState = Dhcp4Stopped;
+ DhcpSb->Xid = NET_RANDOM (NetRandomInitSeed ());
+ CopyMem (
+ &DhcpSb->ServiceBinding,
+ &mDhcp4ServiceBindingTemplate,
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)
+ );
+ //
+ // Create various resources, UdpIo, Timer, and get Mac address
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ DhcpOnTimerTick,
+ DhcpSb,
+ &DhcpSb->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ DhcpSb->UdpIo = UdpIoCreateIo (
+ Controller,
+ ImageHandle,
+ DhcpConfigUdpIo,
+ UDP_IO_UDP4_VERSION,
+ NULL
+ );
+
+ if (DhcpSb->UdpIo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ DhcpSb->HwLen = (UINT8) DhcpSb->UdpIo->SnpMode.HwAddressSize;
+ DhcpSb->HwType = DhcpSb->UdpIo->SnpMode.IfType;
+ CopyMem (&DhcpSb->Mac, &DhcpSb->UdpIo->SnpMode.CurrentAddress, sizeof (DhcpSb->Mac));
+
+ *Service = DhcpSb;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Dhcp4CloseService (DhcpSb);
+ FreePool (DhcpSb);
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+
+ //
+ // First: test for the DHCP4 Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ASSERT (DhcpSb != NULL);
+
+ //
+ // Start the receiving
+ //
+ Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Dhcp4ServiceBinding Protocol onto ControllerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &DhcpSb->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return Status;
+
+ON_ERROR:
+ Dhcp4CloseService (DhcpSb);
+ FreePool (DhcpSb);
+ return Status;
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DestroyChildEntry (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = NET_LIST_USER_STRUCT_S (Entry, DHCP_PROTOCOL, Link, DHCP_PROTOCOL_SIGNATURE);
+ ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context;
+
+ return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ DHCP_SERVICE *DhcpSb;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ LIST_ENTRY *List;
+ UINTN ListLength;
+
+ //
+ // DHCP driver opens UDP child, So, the ControllerHandle is the
+ // UDP child handle. locate the Nic handle first.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding);
+ if (!IsListEmpty (&DhcpSb->Children)) {
+ //
+ // Destroy all the children instances before destroy the service.
+ //
+ List = &DhcpSb->Children;
+ Status = NetDestroyLinkList (
+ List,
+ Dhcp4DestroyChildEntry,
+ ServiceBinding,
+ &ListLength
+ );
+ if (EFI_ERROR (Status) || ListLength != 0) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+ if (NumberOfChildren == 0 && !IsListEmpty (&DhcpSb->Children)) {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ if (NumberOfChildren == 0 && IsListEmpty (&DhcpSb->Children)) {
+ //
+ // Destroy the service itself if no child instance left.
+ //
+ DhcpSb->ServiceState = DHCP_DESTROY;
+
+ gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ Dhcp4CloseService (DhcpSb);
+
+ if (gDhcpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gDhcpControllerNameTable);
+ gDhcpControllerNameTable = NULL;
+ }
+ FreePool (DhcpSb);
+
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+/**
+ Initialize a new DHCP instance.
+
+ @param DhcpSb The dhcp service instance
+ @param Instance The dhcp instance to initialize
+
+**/
+VOID
+DhcpInitProtocol (
+ IN DHCP_SERVICE *DhcpSb,
+ IN OUT DHCP_PROTOCOL *Instance
+ )
+{
+ Instance->Signature = DHCP_PROTOCOL_SIGNATURE;
+ CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (Instance->Dhcp4Protocol));
+ InitializeListHead (&Instance->Link);
+ Instance->Handle = NULL;
+ Instance->Service = DhcpSb;
+ Instance->InDestroy = FALSE;
+ Instance->CompletionEvent = NULL;
+ Instance->RenewRebindEvent = NULL;
+ Instance->Token = NULL;
+ Instance->UdpIo = NULL;
+ Instance->ElaspedTime = 0;
+ NetbufQueInit (&Instance->ResponseQueue);
+}
+
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCESS The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ VOID *Udp4;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = AllocatePool (sizeof (*Instance));
+
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DhcpSb = DHCP_SERVICE_FROM_THIS (This);
+ DhcpInitProtocol (DhcpSb, Instance);
+
+ //
+ // Install DHCP4 onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ &Instance->Dhcp4Protocol,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Instance);
+ return Status;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Udp4 protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ DhcpSb->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiDhcp4ProtocolGuid,
+ &Instance->Dhcp4Protocol,
+ NULL
+ );
+
+ FreePool (Instance);
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&DhcpSb->Children, &Instance->Link);
+ DhcpSb->NumChildren++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCESS The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_DHCP4_PROTOCOL *Dhcp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **) &Dhcp,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (Dhcp);
+ DhcpSb = DHCP_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != DhcpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // A child can be destroyed more than once. For example,
+ // Dhcp4DriverBindingStop will destroy all of its children.
+ // when caller driver is being stopped, it will destroy the
+ // dhcp child it opens.
+ //
+ if (Instance->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->InDestroy = TRUE;
+
+ //
+ // Close the Udp4 protocol.
+ //
+ gBS->CloseProtocol (
+ DhcpSb->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gDhcp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the DHCP4 protocol first to enable a top down destruction.
+ //
+ gBS->RestoreTPL (OldTpl);
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiDhcp4ProtocolGuid,
+ Dhcp
+ );
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (EFI_ERROR (Status)) {
+ Instance->InDestroy = FALSE;
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ if (DhcpSb->ActiveChild == Instance) {
+ DhcpYieldControl (DhcpSb);
+ }
+
+ RemoveEntryList (&Instance->Link);
+ DhcpSb->NumChildren--;
+
+ if (Instance->UdpIo != NULL) {
+ UdpIoCleanIo (Instance->UdpIo);
+ gBS->CloseProtocol (
+ Instance->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ Instance->Service->Image,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ Instance->Token = NULL;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Instance);
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Driver.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Driver.h
new file mode 100644
index 00000000..124f47d5
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Driver.h
@@ -0,0 +1,146 @@
+/** @file
+ Header for the DHCP4 driver.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_DHCP4_DRIVER_H__
+#define __EFI_DHCP4_DRIVER_H__
+
+extern EFI_COMPONENT_NAME_PROTOCOL gDhcp4ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp4ComponentName2;
+extern EFI_UNICODE_STRING_TABLE *gDhcpControllerNameTable;
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device
+ @retval EFI_ALREADY_STARTED This driver is already running on this device
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCESS The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCESS The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Dxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Dxe.inf
new file mode 100644
index 00000000..c518cbdf
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Dxe.inf
@@ -0,0 +1,66 @@
+## @file
+# This module produces EFI DHCPv4 Protocol and EFI DHCPv4 Service Binding Protocol.
+#
+# This module produces EFI DHCPv4 Protocol upon EFI UDPv4 Protocol, to provide the
+# capability to collect configuration information for the EFI IPv4 Protocol drivers
+# and to provide DHCPv4 server and PXE boot server discovery services.
+#
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Dhcp4Dxe
+ MODULE_UNI_FILE = Dhcp4Dxe.uni
+ FILE_GUID = 94734718-0BBC-47fb-96A5-EE7A5AE6A2AD
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Dhcp4DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+# DRIVER_BINDING = gDhcp4DriverBinding
+# COMPONENT_NAME = gDhcp4ComponentName
+# COMPONENT_NAME2 = gDhcp4ComponentName2
+#
+
+[Sources]
+ Dhcp4Impl.c
+ Dhcp4Io.c
+ Dhcp4Io.h
+ ComponentName.c
+ Dhcp4Driver.h
+ Dhcp4Driver.c
+ Dhcp4Option.c
+ Dhcp4Option.h
+ Dhcp4Impl.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ NetworkPkg/NetworkPkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ UdpIoLib
+
+
+[Protocols]
+ gEfiDhcp4ServiceBindingProtocolGuid ## BY_START
+ gEfiUdp4ServiceBindingProtocolGuid ## TO_START
+ gEfiDhcp4ProtocolGuid ## BY_START
+ gEfiUdp4ProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ Dhcp4DxeExtra.uni
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Dxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Dxe.uni
new file mode 100644
index 00000000..5405b88d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Dxe.uni
@@ -0,0 +1,18 @@
+// /** @file
+// This module produces EFI DHCPv4 Protocol and EFI DHCPv4 Service Binding Protocol.
+//
+// This module produces EFI DHCPv4 Protocol upon EFI UDPv4 Protocol, to provide the
+// capability to collect configuration information for the EFI IPv4 Protocol drivers
+// and to provide DHCPv4 server and PXE boot server discovery services.
+//
+// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "EFI DHCPv4 Driver"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI DHCPv4 Protocol using the EFI UDPv4 Protocol, providing the capability to collect configuration information for the EFI IPv4 Protocol drivers and providing DHCPv4 server and PXE boot server discovery services."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4DxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4DxeExtra.uni
new file mode 100644
index 00000000..ca62553f
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4DxeExtra.uni
@@ -0,0 +1,14 @@
+// /** @file
+// Dhcp4Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"DHCP v4 DXE Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Impl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Impl.c
new file mode 100644
index 00000000..001e8d76
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Impl.c
@@ -0,0 +1,1802 @@
+/** @file
+ This file implement the EFI_DHCP4_PROTOCOL interface.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+/**
+ Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver.
+
+ The GetModeData() function returns the current operating mode and cached data
+ packet for the EFI DHCPv4 Protocol driver.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure.
+
+ @retval EFI_SUCCESS The mode data was returned.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4GetModeData (
+ IN EFI_DHCP4_PROTOCOL *This,
+ OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData
+ );
+
+/**
+ Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver.
+
+ The Configure() function is used to initialize, change, or reset the operational
+ settings of the EFI DHCPv4 Protocol driver for the communication device on which
+ the EFI DHCPv4 Service Binding Protocol is installed. This function can be
+ successfully called only if both of the following are true:
+ * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init,
+ Dhcp4InitReboot, or Dhcp4Bound states.
+ * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI
+ DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4
+ Protocol driver.
+ When this driver is in the Dhcp4Stopped state, it can transfer into one of the
+ following two possible initial states:
+ * Dhcp4Init
+ * Dhcp4InitReboot
+ The driver can transfer into these states by calling Configure() with a non-NULL
+ Dhcp4CfgData. The driver will transfer into the appropriate state based on the
+ supplied client network address in the ClientAddress parameter and DHCP options
+ in the OptionList parameter as described in RFC 2131.
+ When Configure() is called successfully while Dhcp4CfgData is set to NULL, the
+ default configuring data will be reset in the EFI DHCPv4 Protocol driver and
+ the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance
+ wants to make it possible for another instance to configure the EFI DHCPv4 Protocol
+ driver, it must call this function with Dhcp4CfgData set to NULL.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] Dhcp4CfgData Pointer to the EFI_DHCP4_CONFIG_DATA.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or
+ Dhcp4InitReboot state, if the original state of this driver
+ was Dhcp4Stopped and the value of Dhcp4CfgData was
+ not NULL. Otherwise, the state was left unchanged.
+ @retval EFI_ACCESS_DENIED This instance of the EFI DHCPv4 Protocol driver was not in the
+ Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state;
+ Or another instance of this EFI DHCPv4 Protocol driver is already
+ in a valid configured state.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Configure (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL
+ );
+
+/**
+ Starts the DHCP configuration process.
+
+ The Start() function starts the DHCP configuration process. This function can
+ be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
+ Dhcp4InitReboot state.
+ If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
+ driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
+ Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
+ If the process aborts, either by the user or by some unexpected network error,
+ the state is restored to the Dhcp4Init state. The Start() function can be called
+ again to restart the process.
+ Refer to RFC 2131 for precise state transitions during this process. At the
+ time when each event occurs in this process, the callback function that was set
+ by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
+ opportunity to control the process.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] CompletionEvent If not NULL, indicates the event that will be signaled when the
+ EFI DHCPv4 Protocol driver is transferred into the
+ Dhcp4Bound state or when the DHCP process is aborted.
+ EFI_DHCP4_PROTOCOL.GetModeData() can be called to
+ check the completion status. If NULL,
+ EFI_DHCP4_PROTOCOL.Start() will wait until the driver
+ is transferred into the Dhcp4Bound state or the process fails.
+
+ @retval EFI_SUCCESS The DHCP configuration process has started, or it has completed
+ when CompletionEvent is NULL.
+ @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
+ state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TIMEOUT The DHCP configuration process failed because no response was
+ received from the server within the specified timeout value.
+ @retval EFI_ABORTED The user aborted the DHCP process.
+ @retval EFI_ALREADY_STARTED Some other EFI DHCPv4 Protocol instance already started the
+ DHCP process.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Start (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ );
+
+/**
+ Extends the lease time by sending a request packet.
+
+ The RenewRebind() function is used to manually extend the lease time when the
+ EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
+ not expired yet. This function will send a request packet to the previously
+ found server (or to any server when RebindRequest is TRUE) and transfer the
+ state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
+ TRUE). When a response is received, the state is returned to Dhcp4Bound.
+ If no response is received before the try count is exceeded (the RequestTryCount
+ field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
+ was issued by the previous server expires, the driver will return to the Dhcp4Bound
+ state and the previous configuration is restored. The outgoing and incoming packets
+ can be captured by the EFI_DHCP4_CALLBACK function.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] RebindRequest If TRUE, this function broadcasts the request packets and enters
+ the Dhcp4Rebinding state. Otherwise, it sends a unicast
+ request packet and enters the Dhcp4Renewing state.
+ @param[in] CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
+ completes or some error occurs.
+ EFI_DHCP4_PROTOCOL.GetModeData() can be called to
+ check the completion status. If NULL,
+ EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
+ until the DHCP process finishes.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the
+ Dhcp4Renewing state or is back to the Dhcp4Bound state.
+ @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
+ state. EFI_DHCP4_PROTOCOL.Configure() needs to
+ be called.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_TIMEOUT There was no response from the server when the try count was
+ exceeded.
+ @retval EFI_ACCESS_DENIED The driver is not in the Dhcp4Bound state.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4RenewRebind (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN BOOLEAN RebindRequest,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ );
+
+/**
+ Releases the current address configuration.
+
+ The Release() function releases the current configured IP address by doing either
+ of the following:
+ * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
+ Dhcp4Bound state
+ * Setting the previously assigned IP address that was provided with the
+ EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
+ Dhcp4InitReboot state
+ After a successful call to this function, the EFI DHCPv4 Protocol driver returns
+ to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Release (
+ IN EFI_DHCP4_PROTOCOL *This
+ );
+
+/**
+ Stops the current address configuration.
+
+ The Stop() function is used to stop the DHCP configuration process. After this
+ function is called successfully, the EFI DHCPv4 Protocol driver is transferred
+ into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
+ before DHCP configuration process can be started again. This function can be
+ called when the EFI DHCPv4 Protocol driver is in any state.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Stop (
+ IN EFI_DHCP4_PROTOCOL *This
+ );
+
+/**
+ Builds a DHCP packet, given the options to be appended or deleted or replaced.
+
+ The Build() function is used to assemble a new packet from the original packet
+ by replacing or deleting existing options or appending new options. This function
+ does not change any state of the EFI DHCPv4 Protocol driver and can be used at
+ any time.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] SeedPacket Initial packet to be used as a base for building new packet.
+ @param[in] DeleteCount Number of opcodes in the DeleteList.
+ @param[in] DeleteList List of opcodes to be deleted from the seed packet.
+ Ignored if DeleteCount is zero.
+ @param[in] AppendCount Number of entries in the OptionList.
+ @param[in] AppendList Pointer to a DHCP option list to be appended to SeedPacket.
+ If SeedPacket also contains options in this list, they are
+ replaced by new options (except pad option). Ignored if
+ AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
+ @param[out] NewPacket Pointer to storage for the pointer to the new allocated packet.
+ Use the EFI Boot Service FreePool() on the resulting pointer
+ when done with the packet.
+
+ @retval EFI_SUCCESS The new packet was built.
+ @retval EFI_OUT_OF_RESOURCES Storage for the new packet could not be allocated.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Build (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ );
+
+/**
+ Transmits a DHCP formatted packet and optionally waits for responses.
+
+ The TransmitReceive() function is used to transmit a DHCP packet and optionally
+ wait for the response from servers. This function does not change the state of
+ the EFI DHCPv4 Protocol driver and thus can be used at any time.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] Token Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
+
+ @retval EFI_SUCCESS The packet was successfully queued for transmission.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call
+ this function after collection process completes.
+ @retval EFI_NO_MAPPING The default station address is not available yet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Some other unexpected error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4TransmitReceive (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token
+ );
+
+/**
+ Parses the packed DHCP option data.
+
+ The Parse() function is used to retrieve the option list from a DHCP packet.
+ If *OptionCount isn't zero, and there is enough space for all the DHCP options
+ in the Packet, each element of PacketOptionList is set to point to somewhere in
+ the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
+ the caller should reassemble the parsed DHCP options to get the finial result.
+ If *OptionCount is zero or there isn't enough space for all of them, the number
+ of DHCP options in the Packet is returned in OptionCount.
+
+ @param This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param Packet Pointer to packet to be parsed.
+ @param OptionCount On input, the number of entries in the PacketOptionList.
+ On output, the number of entries that were written into the
+ PacketOptionList.
+ @param PacketOptionList List of packet option entries to be filled in. End option or pad
+ options are not included.
+
+ @retval EFI_SUCCESS The packet was successfully parsed.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_BUFFER_TOO_SMALL One or more of the following conditions is TRUE:
+ 1) *OptionCount is smaller than the number of options that
+ were found in the Packet.
+ 2) PacketOptionList is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Parse (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ );
+
+EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate = {
+ EfiDhcp4GetModeData,
+ EfiDhcp4Configure,
+ EfiDhcp4Start,
+ EfiDhcp4RenewRebind,
+ EfiDhcp4Release,
+ EfiDhcp4Stop,
+ EfiDhcp4Build,
+ EfiDhcp4TransmitReceive,
+ EfiDhcp4Parse
+};
+
+/**
+ Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver.
+
+ The GetModeData() function returns the current operating mode and cached data
+ packet for the EFI DHCPv4 Protocol driver.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure.
+
+ @retval EFI_SUCCESS The mode data was returned.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4GetModeData (
+ IN EFI_DHCP4_PROTOCOL *This,
+ OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PARAMETER *Para;
+ EFI_TPL OldTpl;
+ IP4_ADDR Ip;
+
+ //
+ // First validate the parameters.
+ //
+ if ((This == NULL) || (Dhcp4ModeData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ //
+ // Caller can use GetModeData to retrieve current DHCP states
+ // no matter whether it is the active child or not.
+ //
+ Dhcp4ModeData->State = (EFI_DHCP4_STATE) DhcpSb->DhcpState;
+ CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (Dhcp4ModeData->ConfigData));
+ CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (Dhcp4ModeData->ClientMacAddress));
+
+ Ip = HTONL (DhcpSb->ClientAddr);
+ CopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Netmask);
+ CopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->ServerAddr);
+ CopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Para = DhcpSb->Para;
+
+ if (Para != NULL) {
+ Ip = HTONL (Para->Router);
+ CopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+ Dhcp4ModeData->LeaseTime = Para->Lease;
+ } else {
+ ZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+ Dhcp4ModeData->LeaseTime = 0xffffffff;
+ }
+
+ Dhcp4ModeData->ReplyPacket = DhcpSb->Selected;
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Free the resource related to the configure parameters.
+ DHCP driver will make a copy of the user's configure
+ such as the time out value.
+
+ @param Config The DHCP configure data
+
+**/
+VOID
+DhcpCleanConfigure (
+ IN OUT EFI_DHCP4_CONFIG_DATA *Config
+ )
+{
+ UINT32 Index;
+
+ if (Config->DiscoverTimeout != NULL) {
+ FreePool (Config->DiscoverTimeout);
+ }
+
+ if (Config->RequestTimeout != NULL) {
+ FreePool (Config->RequestTimeout);
+ }
+
+ if (Config->OptionList != NULL) {
+ for (Index = 0; Index < Config->OptionCount; Index++) {
+ if (Config->OptionList[Index] != NULL) {
+ FreePool (Config->OptionList[Index]);
+ }
+ }
+
+ FreePool (Config->OptionList);
+ }
+
+ ZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA));
+}
+
+
+/**
+ Allocate memory for configure parameter such as timeout value for Dst,
+ then copy the configure parameter from Src to Dst.
+
+ @param[out] Dst The destination DHCP configure data.
+ @param[in] Src The source DHCP configure data.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_SUCCESS The configure is copied.
+
+**/
+EFI_STATUS
+DhcpCopyConfigure (
+ OUT EFI_DHCP4_CONFIG_DATA *Dst,
+ IN EFI_DHCP4_CONFIG_DATA *Src
+ )
+{
+ EFI_DHCP4_PACKET_OPTION **DstOptions;
+ EFI_DHCP4_PACKET_OPTION **SrcOptions;
+ UINTN Len;
+ UINT32 Index;
+
+ CopyMem (Dst, Src, sizeof (*Dst));
+ Dst->DiscoverTimeout = NULL;
+ Dst->RequestTimeout = NULL;
+ Dst->OptionList = NULL;
+
+ //
+ // Allocate a memory then copy DiscoverTimeout to it
+ //
+ if (Src->DiscoverTimeout != NULL) {
+ Len = Src->DiscoverTryCount * sizeof (UINT32);
+ Dst->DiscoverTimeout = AllocatePool (Len);
+
+ if (Dst->DiscoverTimeout == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < Src->DiscoverTryCount; Index++) {
+ Dst->DiscoverTimeout[Index] = MAX (Src->DiscoverTimeout[Index], 1);
+ }
+ }
+
+ //
+ // Allocate a memory then copy RequestTimeout to it
+ //
+ if (Src->RequestTimeout != NULL) {
+ Len = Src->RequestTryCount * sizeof (UINT32);
+ Dst->RequestTimeout = AllocatePool (Len);
+
+ if (Dst->RequestTimeout == NULL) {
+ goto ON_ERROR;
+ }
+
+ for (Index = 0; Index < Src->RequestTryCount; Index++) {
+ Dst->RequestTimeout[Index] = MAX (Src->RequestTimeout[Index], 1);
+ }
+ }
+
+ //
+ // Allocate an array of dhcp option point, then allocate memory
+ // for each option and copy the source option to it
+ //
+ if (Src->OptionList != NULL) {
+ Len = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *);
+ Dst->OptionList = AllocateZeroPool (Len);
+
+ if (Dst->OptionList == NULL) {
+ goto ON_ERROR;
+ }
+
+ DstOptions = Dst->OptionList;
+ SrcOptions = Src->OptionList;
+
+ for (Index = 0; Index < Src->OptionCount; Index++) {
+ Len = sizeof (EFI_DHCP4_PACKET_OPTION) + MAX (SrcOptions[Index]->Length - 1, 0);
+
+ DstOptions[Index] = AllocatePool (Len);
+
+ if (DstOptions[Index] == NULL) {
+ goto ON_ERROR;
+ }
+
+ CopyMem (DstOptions[Index], SrcOptions[Index], Len);
+ }
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ DhcpCleanConfigure (Dst);
+ return EFI_OUT_OF_RESOURCES;
+}
+
+
+/**
+ Give up the control of the DHCP service to let other child
+ resume. Don't change the service's DHCP state and the Client
+ address and option list configure as required by RFC2131.
+
+ @param DhcpSb The DHCP service instance.
+
+**/
+VOID
+DhcpYieldControl (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+
+ Config = &DhcpSb->ActiveConfig;
+
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+ DhcpSb->ActiveChild = NULL;
+
+ if (Config->DiscoverTimeout != NULL) {
+ FreePool (Config->DiscoverTimeout);
+
+ Config->DiscoverTryCount = 0;
+ Config->DiscoverTimeout = NULL;
+ }
+
+ if (Config->RequestTimeout != NULL) {
+ FreePool (Config->RequestTimeout);
+
+ Config->RequestTryCount = 0;
+ Config->RequestTimeout = NULL;
+ }
+
+ Config->Dhcp4Callback = NULL;
+ Config->CallbackContext = NULL;
+}
+
+
+/**
+ Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver.
+
+ The Configure() function is used to initialize, change, or reset the operational
+ settings of the EFI DHCPv4 Protocol driver for the communication device on which
+ the EFI DHCPv4 Service Binding Protocol is installed. This function can be
+ successfully called only if both of the following are true:
+ * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init,
+ Dhcp4InitReboot, or Dhcp4Bound states.
+ * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI
+ DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4
+ Protocol driver.
+ When this driver is in the Dhcp4Stopped state, it can transfer into one of the
+ following two possible initial states:
+ * Dhcp4Init
+ * Dhcp4InitReboot
+ The driver can transfer into these states by calling Configure() with a non-NULL
+ Dhcp4CfgData. The driver will transfer into the appropriate state based on the
+ supplied client network address in the ClientAddress parameter and DHCP options
+ in the OptionList parameter as described in RFC 2131.
+ When Configure() is called successfully while Dhcp4CfgData is set to NULL, the
+ default configuring data will be reset in the EFI DHCPv4 Protocol driver and
+ the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance
+ wants to make it possible for another instance to configure the EFI DHCPv4 Protocol
+ driver, it must call this function with Dhcp4CfgData set to NULL.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] Dhcp4CfgData Pointer to the EFI_DHCP4_CONFIG_DATA.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or
+ Dhcp4InitReboot state, if the original state of this driver
+ was Dhcp4Stopped and the value of Dhcp4CfgData was
+ not NULL. Otherwise, the state was left unchanged.
+ @retval EFI_ACCESS_DENIED This instance of the EFI DHCPv4 Protocol driver was not in the
+ Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state;
+ Or another instance of this EFI DHCPv4 Protocol driver is already
+ in a valid configured state.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Configure (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ UINT32 Index;
+ IP4_ADDR Ip;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp4CfgData != NULL) {
+ if ((Dhcp4CfgData->DiscoverTryCount != 0) && (Dhcp4CfgData->DiscoverTimeout == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Dhcp4CfgData->RequestTryCount != 0) && (Dhcp4CfgData->RequestTimeout == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Dhcp4CfgData->OptionCount != 0) && (Dhcp4CfgData->OptionList == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR));
+ if (IP4_IS_LOCAL_BROADCAST(NTOHL (Ip))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ DhcpSb = Instance->Service;
+ Config = &DhcpSb->ActiveConfig;
+
+ Status = EFI_ACCESS_DENIED;
+
+ if ((DhcpSb->DhcpState != Dhcp4Stopped) &&
+ (DhcpSb->DhcpState != Dhcp4Init) &&
+ (DhcpSb->DhcpState != Dhcp4InitReboot) &&
+ (DhcpSb->DhcpState != Dhcp4Bound)) {
+
+ goto ON_EXIT;
+ }
+
+ if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {
+ goto ON_EXIT;
+ }
+
+ if (Dhcp4CfgData != NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DhcpCleanConfigure (Config);
+
+ if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {
+ goto ON_EXIT;
+ }
+
+ DhcpSb->UserOptionLen = 0;
+
+ for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {
+ DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;
+ }
+
+ DhcpSb->ActiveChild = Instance;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);
+
+ if (DhcpSb->ClientAddr != 0) {
+ DhcpSb->DhcpState = Dhcp4InitReboot;
+ } else {
+ DhcpSb->DhcpState = Dhcp4Init;
+ }
+ }
+
+ DhcpSb->ServiceState = DHCP_CONFIGED;
+ Status = EFI_SUCCESS;
+
+ } else if (DhcpSb->ActiveChild == Instance) {
+ Status = EFI_SUCCESS;
+ DhcpYieldControl (DhcpSb);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Starts the DHCP configuration process.
+
+ The Start() function starts the DHCP configuration process. This function can
+ be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
+ Dhcp4InitReboot state.
+ If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
+ driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
+ Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
+ If the process aborts, either by the user or by some unexpected network error,
+ the state is restored to the Dhcp4Init state. The Start() function can be called
+ again to restart the process.
+ Refer to RFC 2131 for precise state transitions during this process. At the
+ time when each event occurs in this process, the callback function that was set
+ by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
+ opportunity to control the process.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] CompletionEvent If not NULL, indicates the event that will be signaled when the
+ EFI DHCPv4 Protocol driver is transferred into the
+ Dhcp4Bound state or when the DHCP process is aborted.
+ EFI_DHCP4_PROTOCOL.GetModeData() can be called to
+ check the completion status. If NULL,
+ EFI_DHCP4_PROTOCOL.Start() will wait until the driver
+ is transferred into the Dhcp4Bound state or the process fails.
+
+ @retval EFI_SUCCESS The DHCP configuration process has started, or it has completed
+ when CompletionEvent is NULL.
+ @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
+ state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TIMEOUT The DHCP configuration process failed because no response was
+ received from the server within the specified timeout value.
+ @retval EFI_ABORTED The user aborted the DHCP process.
+ @retval EFI_ALREADY_STARTED Some other EFI DHCPv4 Protocol instance already started the
+ DHCP process.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Start (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_STATUS MediaStatus;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ Status = EFI_NOT_STARTED;
+ goto ON_ERROR;
+ }
+
+ if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {
+ Status = EFI_ALREADY_STARTED;
+ goto ON_ERROR;
+ }
+
+ //
+ // Check Media Status.
+ //
+ MediaStatus = EFI_SUCCESS;
+ NetLibDetectMediaWaitTimeout (DhcpSb->Controller, DHCP_CHECK_MEDIA_WAITING_TIME, &MediaStatus);
+ if (MediaStatus != EFI_SUCCESS) {
+ Status = EFI_NO_MEDIA;
+ goto ON_ERROR;
+ }
+
+ DhcpSb->IoStatus = EFI_ALREADY_STARTED;
+
+ if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {
+ goto ON_ERROR;
+ }
+
+
+ Instance->CompletionEvent = CompletionEvent;
+
+ //
+ // Restore the TPL now, don't call poll function at TPL_CALLBACK.
+ //
+ gBS->RestoreTPL (OldTpl);
+
+ if (CompletionEvent == NULL) {
+ while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
+ DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
+ }
+
+ return DhcpSb->IoStatus;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Extends the lease time by sending a request packet.
+
+ The RenewRebind() function is used to manually extend the lease time when the
+ EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
+ not expired yet. This function will send a request packet to the previously
+ found server (or to any server when RebindRequest is TRUE) and transfer the
+ state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
+ TRUE). When a response is received, the state is returned to Dhcp4Bound.
+ If no response is received before the try count is exceeded (the RequestTryCount
+ field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
+ was issued by the previous server expires, the driver will return to the Dhcp4Bound
+ state and the previous configuration is restored. The outgoing and incoming packets
+ can be captured by the EFI_DHCP4_CALLBACK function.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] RebindRequest If TRUE, this function broadcasts the request packets and enters
+ the Dhcp4Rebinding state. Otherwise, it sends a unicast
+ request packet and enters the Dhcp4Renewing state.
+ @param[in] CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
+ completes or some error occurs.
+ EFI_DHCP4_PROTOCOL.GetModeData() can be called to
+ check the completion status. If NULL,
+ EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
+ until the DHCP process finishes.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the
+ Dhcp4Renewing state or is back to the Dhcp4Bound state.
+ @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
+ state. EFI_DHCP4_PROTOCOL.Configure() needs to
+ be called.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_TIMEOUT There was no response from the server when the try count was
+ exceeded.
+ @retval EFI_ACCESS_DENIED The driver is not in the Dhcp4Bound state.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4RenewRebind (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN BOOLEAN RebindRequest,
+ IN EFI_EVENT CompletionEvent OPTIONAL
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (DhcpSb->DhcpState != Dhcp4Bound) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ if (DHCP_IS_BOOTP (DhcpSb->Para)) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Transit the states then send a extra DHCP request
+ //
+ if (!RebindRequest) {
+ DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
+ } else {
+ DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
+ }
+
+ //
+ // Clear initial time to make sure that elapsed-time
+ // is set to 0 for first REQUEST in renewal process.
+ //
+ Instance->ElaspedTime = 0;
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ (UINT8 *) "Extra renew/rebind by the application"
+ );
+
+ if (EFI_ERROR (Status)) {
+ DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
+ goto ON_EXIT;
+ }
+
+ DhcpSb->ExtraRefresh = TRUE;
+ DhcpSb->IoStatus = EFI_ALREADY_STARTED;
+ Instance->RenewRebindEvent = CompletionEvent;
+
+ gBS->RestoreTPL (OldTpl);
+
+ if (CompletionEvent == NULL) {
+ while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
+ DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
+
+ }
+
+ return DhcpSb->IoStatus;
+ }
+
+ return EFI_SUCCESS;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Releases the current address configuration.
+
+ The Release() function releases the current configured IP address by doing either
+ of the following:
+ * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
+ Dhcp4Bound state
+ * Setting the previously assigned IP address that was provided with the
+ EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
+ Dhcp4InitReboot state
+ After a successful call to this function, the EFI DHCPv4 Protocol driver returns
+ to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Release (
+ IN EFI_DHCP4_PROTOCOL *This
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_RELEASE,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ }
+
+ DhcpCleanLease (DhcpSb);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Stops the current address configuration.
+
+ The Stop() function is used to stop the DHCP configuration process. After this
+ function is called successfully, the EFI DHCPv4 Protocol driver is transferred
+ into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
+ before DHCP configuration process can be started again. This function can be
+ called when the EFI DHCPv4 Protocol driver is in any state.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Stop (
+ IN EFI_DHCP4_PROTOCOL *This
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_TPL OldTpl;
+
+ //
+ // First validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ DhcpSb = Instance->Service;
+
+ DhcpCleanLease (DhcpSb);
+
+ DhcpSb->DhcpState = Dhcp4Stopped;
+ DhcpSb->ServiceState = DHCP_UNCONFIGED;
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Builds a DHCP packet, given the options to be appended or deleted or replaced.
+
+ The Build() function is used to assemble a new packet from the original packet
+ by replacing or deleting existing options or appending new options. This function
+ does not change any state of the EFI DHCPv4 Protocol driver and can be used at
+ any time.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] SeedPacket Initial packet to be used as a base for building new packet.
+ @param[in] DeleteCount Number of opcodes in the DeleteList.
+ @param[in] DeleteList List of opcodes to be deleted from the seed packet.
+ Ignored if DeleteCount is zero.
+ @param[in] AppendCount Number of entries in the OptionList.
+ @param[in] AppendList Pointer to a DHCP option list to be appended to SeedPacket.
+ If SeedPacket also contains options in this list, they are
+ replaced by new options (except pad option). Ignored if
+ AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
+ @param[out] NewPacket Pointer to storage for the pointer to the new allocated packet.
+ Use the EFI Boot Service FreePool() on the resulting pointer
+ when done with the packet.
+
+ @retval EFI_SUCCESS The new packet was built.
+ @retval EFI_OUT_OF_RESOURCES Storage for the new packet could not be allocated.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Build (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ )
+{
+ //
+ // First validate the parameters
+ //
+ if ((This == NULL) || (NewPacket == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
+ EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((DeleteCount == 0) && (AppendCount == 0)) ||
+ ((DeleteCount != 0) && (DeleteList == NULL)) ||
+ ((AppendCount != 0) && (AppendList == NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return DhcpBuild (
+ SeedPacket,
+ DeleteCount,
+ DeleteList,
+ AppendCount,
+ AppendList,
+ NewPacket
+ );
+}
+
+/**
+ Callback by UdpIoCreatePort() when creating UdpIo for this Dhcp4 instance.
+
+ @param[in] UdpIo The UdpIo being created.
+ @param[in] Context Dhcp4 instance.
+
+ @retval EFI_SUCCESS UdpIo is configured successfully.
+ @retval EFI_INVALID_PARAMETER Class E IP address is not supported or other parameters
+ are not valid.
+ @retval other Other error occurs.
+**/
+EFI_STATUS
+EFIAPI
+Dhcp4InstanceConfigUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ DHCP_SERVICE *DhcpSb;
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+ EFI_UDP4_CONFIG_DATA UdpConfigData;
+ IP4_ADDR ClientAddr;
+ IP4_ADDR Ip;
+ INTN Class;
+ IP4_ADDR SubnetMask;
+
+ Instance = (DHCP_PROTOCOL *) Context;
+ DhcpSb = Instance->Service;
+ Token = Instance->Token;
+
+ ZeroMem (&UdpConfigData, sizeof (EFI_UDP4_CONFIG_DATA));
+
+ UdpConfigData.AcceptBroadcast = TRUE;
+ UdpConfigData.AllowDuplicatePort = TRUE;
+ UdpConfigData.TimeToLive = 64;
+ UdpConfigData.DoNotFragment = TRUE;
+
+ ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
+ Ip = HTONL (ClientAddr);
+ CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ if (DhcpSb->Netmask == 0) {
+ //
+ // The Dhcp4.TransmitReceive() API should be able to used at any time according to
+ // UEFI spec, while in classless addressing network, the netmask must be explicitly
+ // provided together with the station address.
+ // If the DHCP instance haven't be configured with a valid netmask, we could only
+ // compute it according to the classful addressing rule.
+ //
+ Class = NetGetIpClass (ClientAddr);
+ //
+ // Class E IP address is not supported here!
+ //
+ ASSERT (Class < IP4_ADDR_CLASSE);
+ if (Class >= IP4_ADDR_CLASSE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ SubnetMask = gIp4AllMasks[Class << 3];
+ } else {
+ SubnetMask = DhcpSb->Netmask;
+ }
+
+ Ip = HTONL (SubnetMask);
+ CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) {
+ UdpConfigData.StationPort = DHCP_CLIENT_PORT;
+ } else {
+ UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort;
+ }
+
+ return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
+}
+
+/**
+ Create UdpIo for this Dhcp4 instance.
+
+ @param Instance The Dhcp4 instance.
+
+ @retval EFI_SUCCESS UdpIo is created successfully.
+ @retval EFI_OUT_OF_RESOURCES Fails to create UdpIo because of limited
+ resources or configuration failure.
+**/
+EFI_STATUS
+Dhcp4InstanceCreateUdpIo (
+ IN OUT DHCP_PROTOCOL *Instance
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ VOID *Udp4;
+
+ ASSERT (Instance->Token != NULL);
+
+ DhcpSb = Instance->Service;
+ Instance->UdpIo = UdpIoCreateIo (
+ DhcpSb->Controller,
+ DhcpSb->Image,
+ Dhcp4InstanceConfigUdpIo,
+ UDP_IO_UDP4_VERSION,
+ Instance
+ );
+ if (Instance->UdpIo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ } else {
+ Status = gBS->OpenProtocol (
+ Instance->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ Instance->Service->Image,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ }
+ return Status;
+ }
+}
+
+/**
+ Callback of Dhcp packet. Does nothing.
+
+ @param Arg The context.
+
+**/
+VOID
+EFIAPI
+DhcpDummyExtFree (
+ IN VOID *Arg
+ )
+{
+}
+
+/**
+ Callback of UdpIoRecvDatagram() that handles a Dhcp4 packet.
+
+ Only BOOTP responses will be handled that correspond to the Xid of the request
+ sent out. The packet will be queued to the response queue.
+
+ @param UdpPacket The Dhcp4 packet.
+ @param EndPoint Udp4 address pair.
+ @param IoStatus Status of the input.
+ @param Context Extra info for the input.
+
+**/
+VOID
+EFIAPI
+PxeDhcpInput (
+ NET_BUF *UdpPacket,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ EFI_DHCP4_HEADER *Head;
+ NET_BUF *Wrap;
+ EFI_DHCP4_PACKET *Packet;
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+ UINT32 Len;
+ EFI_STATUS Status;
+
+ Wrap = NULL;
+ Instance = (DHCP_PROTOCOL *) Context;
+ Token = Instance->Token;
+
+ //
+ // Don't restart receive if error occurs or DHCP is destroyed.
+ //
+ if (EFI_ERROR (IoStatus)) {
+ return ;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ //
+ // Validate the packet received
+ //
+ if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
+ goto RESTART;
+ }
+
+ //
+ // Copy the DHCP message to a continuous memory block, make the buffer size
+ // of the EFI_DHCP4_PACKET a multiple of 4-byte.
+ //
+ Len = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);
+ Wrap = NetbufAlloc (Len);
+ if (Wrap == NULL) {
+ goto RESTART;
+ }
+
+ Packet = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);
+ ASSERT (Packet != NULL);
+
+ Packet->Size = Len;
+ Head = &Packet->Dhcp4.Header;
+ Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
+
+ if (Packet->Length != UdpPacket->TotalSize) {
+ goto RESTART;
+ }
+
+ //
+ // Is this packet the answer to our packet?
+ //
+ if ((Head->OpCode != BOOTP_REPLY) ||
+ (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||
+ (CompareMem (&Token->Packet->Dhcp4.Header.ClientHwAddr[0], Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the options and retrieve the interested options
+ //
+ if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
+ (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
+ EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
+
+ goto RESTART;
+ }
+
+ //
+ // Keep this packet in the ResponseQueue.
+ //
+ NET_GET_REF (Wrap);
+ NetbufQueAppend (&Instance->ResponseQueue, Wrap);
+
+RESTART:
+
+ NetbufFree (UdpPacket);
+
+ if (Wrap != NULL) {
+ NetbufFree (Wrap);
+ }
+
+ Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
+ if (EFI_ERROR (Status)) {
+ PxeDhcpDone (Instance);
+ }
+}
+
+/**
+ Complete a Dhcp4 transaction and signal the upper layer.
+
+ @param Instance Dhcp4 instance.
+
+**/
+VOID
+PxeDhcpDone (
+ IN DHCP_PROTOCOL *Instance
+ )
+{
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+
+ Token = Instance->Token;
+
+ Token->ResponseCount = Instance->ResponseQueue.BufNum;
+ if (Token->ResponseCount != 0) {
+ Token->ResponseList = (EFI_DHCP4_PACKET *) AllocatePool (Instance->ResponseQueue.BufSize);
+ if (Token->ResponseList == NULL) {
+ Token->Status = EFI_OUT_OF_RESOURCES;
+ goto SIGNAL_USER;
+ }
+
+ //
+ // Copy the received DHCP responses.
+ //
+ NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);
+ Token->Status = EFI_SUCCESS;
+ } else {
+ Token->ResponseList = NULL;
+ Token->Status = EFI_TIMEOUT;
+ }
+
+SIGNAL_USER:
+ //
+ // Clean up the resources dedicated for this transmit receive transaction.
+ //
+ NetbufQueFlush (&Instance->ResponseQueue);
+ UdpIoCleanIo (Instance->UdpIo);
+ gBS->CloseProtocol (
+ Instance->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ Instance->Service->Image,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ Instance->Token = NULL;
+
+ if (Token->CompletionEvent != NULL) {
+ gBS->SignalEvent (Token->CompletionEvent);
+ }
+}
+
+
+/**
+ Transmits a DHCP formatted packet and optionally waits for responses.
+
+ The TransmitReceive() function is used to transmit a DHCP packet and optionally
+ wait for the response from servers. This function does not change the state of
+ the EFI DHCPv4 Protocol driver and thus can be used at any time.
+
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param[in] Token Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
+
+ @retval EFI_SUCCESS The packet was successfully queued for transmission.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call
+ this function after collection process completes.
+ @retval EFI_NO_MAPPING The default station address is not available yet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Some other unexpected error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4TransmitReceive (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token
+ )
+{
+ DHCP_PROTOCOL *Instance;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ NET_FRAGMENT Frag;
+ NET_BUF *Wrap;
+ UDP_END_POINT EndPoint;
+ IP4_ADDR Ip;
+ DHCP_SERVICE *DhcpSb;
+ EFI_IP_ADDRESS Gateway;
+ IP4_ADDR ClientAddr;
+
+ if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP_INSTANCE_FROM_THIS (This);
+ DhcpSb = Instance->Service;
+
+ if (Instance->Token != NULL) {
+ //
+ // The previous call to TransmitReceive is not finished.
+ //
+ return EFI_NOT_READY;
+ }
+
+ if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
+ (NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) ||
+ (Token->TimeoutValue == 0) ||
+ ((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL)) ||
+ EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL)) ||
+ EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr)
+ ) {
+ //
+ // The DHCP packet isn't well-formed, the Transaction ID is already used,
+ // the timeout value is zero, the ListenPoint is invalid, or the
+ // RemoteAddress is zero.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
+
+ if (ClientAddr == 0) {
+ return EFI_NO_MAPPING;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Save the token and the timeout value.
+ //
+ Instance->Token = Token;
+ Instance->Timeout = Token->TimeoutValue;
+
+ //
+ // Create a UDP IO for this transmit receive transaction.
+ //
+ Status = Dhcp4InstanceCreateUdpIo (Instance);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Save the Client Address is sent out
+ //
+ CopyMem (
+ &DhcpSb->ClientAddressSendOut[0],
+ &Token->Packet->Dhcp4.Header.ClientHwAddr[0],
+ Token->Packet->Dhcp4.Header.HwAddrLen
+ );
+
+ //
+ // Wrap the DHCP packet into a net buffer.
+ //
+ Frag.Bulk = (UINT8 *) &Token->Packet->Dhcp4;
+ Frag.Len = Token->Packet->Length;
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
+ if (Wrap == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Set the local address and local port to ZERO.
+ //
+ ZeroMem (&EndPoint, sizeof (UDP_END_POINT));
+
+ //
+ // Set the destination address and destination port.
+ //
+ CopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
+ EndPoint.RemoteAddr.Addr[0] = NTOHL (Ip);
+
+ if (Token->RemotePort == 0) {
+ EndPoint.RemotePort = DHCP_SERVER_PORT;
+ } else {
+ EndPoint.RemotePort = Token->RemotePort;
+ }
+
+ //
+ // Get the gateway.
+ //
+ ZeroMem (&Gateway, sizeof (Gateway));
+ if (!IP4_NET_EQUAL (ClientAddr, EndPoint.RemoteAddr.Addr[0], DhcpSb->Netmask)) {
+ CopyMem (&Gateway.v4, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+ Gateway.Addr[0] = NTOHL (Gateway.Addr[0]);
+ }
+
+ //
+ // Transmit the DHCP packet.
+ //
+ Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, &Gateway, DhcpOnPacketSent, NULL);
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Wrap);
+ goto ON_ERROR;
+ }
+
+ //
+ // Start to receive the DHCP response.
+ //
+ Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ON_ERROR:
+
+ if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) {
+ UdpIoCleanIo (Instance->UdpIo);
+ gBS->CloseProtocol (
+ Instance->UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ Instance->Service->Image,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ Instance->Token = NULL;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) {
+ //
+ // Keep polling until timeout if no error happens and the CompletionEvent
+ // is NULL.
+ //
+ while (TRUE) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ //
+ // Raise TPL to protect the UDPIO in instance, in case that DhcpOnTimerTick
+ // free it when timeout.
+ //
+ if (Instance->Timeout > 0) {
+ Instance->UdpIo->Protocol.Udp4->Poll (Instance->UdpIo->Protocol.Udp4);
+ gBS->RestoreTPL (OldTpl);
+ } else {
+ gBS->RestoreTPL (OldTpl);
+ break;
+ }
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Callback function for DhcpIterateOptions. This callback sets the
+ EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point
+ the individual DHCP option in the packet.
+
+ @param[in] Tag The DHCP option type
+ @param[in] Len Length of the DHCP option data
+ @param[in] Data The DHCP option data
+ @param[in] Context The context, to pass several parameters in.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS
+
+**/
+EFI_STATUS
+Dhcp4ParseCheckOption (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_PARSE_CONTEXT *Parse;
+
+ Parse = (DHCP_PARSE_CONTEXT *) Context;
+ Parse->Index++;
+
+ if (Parse->Index <= Parse->OptionCount) {
+ //
+ // Use BASE_CR to get the memory position of EFI_DHCP4_PACKET_OPTION for
+ // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only
+ // pass in the point to option data.
+ //
+ Parse->Option[Parse->Index - 1] = BASE_CR (Data, EFI_DHCP4_PACKET_OPTION, Data);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parses the packed DHCP option data.
+
+ The Parse() function is used to retrieve the option list from a DHCP packet.
+ If *OptionCount isn't zero, and there is enough space for all the DHCP options
+ in the Packet, each element of PacketOptionList is set to point to somewhere in
+ the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
+ the caller should reassemble the parsed DHCP options to get the finial result.
+ If *OptionCount is zero or there isn't enough space for all of them, the number
+ of DHCP options in the Packet is returned in OptionCount.
+
+ @param This Pointer to the EFI_DHCP4_PROTOCOL instance.
+ @param Packet Pointer to packet to be parsed.
+ @param OptionCount On input, the number of entries in the PacketOptionList.
+ On output, the number of entries that were written into the
+ PacketOptionList.
+ @param PacketOptionList List of packet option entries to be filled in. End option or pad
+ options are not included.
+
+ @retval EFI_SUCCESS The packet was successfully parsed.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_BUFFER_TOO_SMALL One or more of the following conditions is TRUE:
+ 1) *OptionCount is smaller than the number of options that
+ were found in the Packet.
+ 2) PacketOptionList is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp4Parse (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ )
+{
+ DHCP_PARSE_CONTEXT Context;
+ EFI_STATUS Status;
+
+ //
+ // First validate the parameters
+ //
+ if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||
+ (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
+ EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ ZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
+
+ Context.Option = PacketOptionList;
+ Context.OptionCount = *OptionCount;
+ Context.Index = 0;
+
+ Status = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *OptionCount = Context.Index;
+
+ if (Context.Index > Context.OptionCount) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set the elapsed time based on the given instance and the pointer to the
+ elapsed time option.
+
+ @param[in] Elapsed The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp4 instance.
+**/
+VOID
+SetElapsedTime (
+ IN UINT16 *Elapsed,
+ IN DHCP_PROTOCOL *Instance
+ )
+{
+ WriteUnaligned16 (Elapsed, HTONS(Instance->ElaspedTime));
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Impl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Impl.h
new file mode 100644
index 00000000..81413780
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Impl.h
@@ -0,0 +1,206 @@
+/** @file
+ 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.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_DHCP4_IMPL_H__
+#define __EFI_DHCP4_IMPL_H__
+
+
+
+#include <Uefi.h>
+
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Udp4.h>
+#include <IndustryStandard/Dhcp.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+
+typedef struct _DHCP_SERVICE DHCP_SERVICE;
+typedef struct _DHCP_PROTOCOL DHCP_PROTOCOL;
+
+#include "Dhcp4Option.h"
+#include "Dhcp4Io.h"
+
+#define DHCP_SERVICE_SIGNATURE SIGNATURE_32 ('D', 'H', 'C', 'P')
+#define DHCP_PROTOCOL_SIGNATURE SIGNATURE_32 ('d', 'h', 'c', 'p')
+
+#define DHCP_CHECK_MEDIA_WAITING_TIME EFI_TIMER_PERIOD_SECONDS(20)
+
+//
+// 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 DESTROY if it is (partly)
+// destroyed.
+//
+#define DHCP_UNCONFIGED 0
+#define DHCP_CONFIGED 1
+#define DHCP_DESTROY 2
+
+
+struct _DHCP_PROTOCOL {
+ UINT32 Signature;
+ EFI_DHCP4_PROTOCOL Dhcp4Protocol;
+ LIST_ENTRY Link;
+ EFI_HANDLE Handle;
+ DHCP_SERVICE *Service;
+
+ BOOLEAN InDestroy;
+
+ EFI_EVENT CompletionEvent;
+ EFI_EVENT RenewRebindEvent;
+
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
+ UDP_IO *UdpIo; // The UDP IO used for TransmitReceive.
+ UINT32 Timeout;
+ UINT16 ElaspedTime;
+ NET_BUF_QUEUE ResponseQueue;
+};
+
+//
+// DHCP driver is specical in that it is a singleton. Although it
+// has a service binding, there can be only one active child.
+//
+struct _DHCP_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ INTN ServiceState; // CONFIGED, UNCONFIGED, and DESTROY
+
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ 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 *UdpIo; // Udp child receiving all DHCP message
+ UDP_IO *LeaseIoPort; // Udp child with lease IP
+ EFI_DHCP4_PACKET *LastPacket; // The last sent packet for retransmission
+ EFI_MAC_ADDRESS Mac;
+ UINT8 HwType;
+ UINT8 HwLen;
+ UINT8 ClientAddressSendOut[16];
+
+ 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
+ UINT32 LastTimeout; // Record the init value of PacketToLive every time
+ INTN CurRetry;
+ INTN MaxRetries;
+ 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;
+
+/**
+ Give up the control of the DHCP service to let other child
+ resume. Don't change the service's DHCP state and the Client
+ address and option list configure as required by RFC2131.
+
+ @param DhcpSb The DHCP service instance.
+
+**/
+VOID
+DhcpYieldControl (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+/**
+ Complete a Dhcp4 transaction and signal the upper layer.
+
+ @param Instance Dhcp4 instance.
+
+**/
+VOID
+PxeDhcpDone (
+ IN DHCP_PROTOCOL *Instance
+ );
+
+/**
+ Free the resource related to the configure parameters.
+ DHCP driver will make a copy of the user's configure
+ such as the time out value.
+
+ @param Config The DHCP configure data
+
+**/
+VOID
+DhcpCleanConfigure (
+ IN OUT EFI_DHCP4_CONFIG_DATA *Config
+ );
+
+/**
+ Callback of Dhcp packet. Does nothing.
+
+ @param Arg The context.
+
+**/
+VOID
+EFIAPI
+DhcpDummyExtFree (
+ IN VOID *Arg
+ );
+
+/**
+ Set the elapsed time based on the given instance and the pointer to the
+ elapsed time option.
+
+ @param[in] Elapsed The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp4 instance.
+**/
+VOID
+SetElapsedTime (
+ IN UINT16 *Elapsed,
+ IN DHCP_PROTOCOL *Instance
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Io.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Io.c
new file mode 100644
index 00000000..00ae7c76
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Io.c
@@ -0,0 +1,1657 @@
+/** @file
+ EFI DHCP protocol implementation.
+
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "Dhcp4Impl.h"
+
+UINT32 mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 };
+
+
+/**
+ Send an initial DISCOVER or REQUEST message according to the
+ DHCP service's current state.
+
+ @param[in] DhcpSb The DHCP service instance
+
+ @retval EFI_SUCCESS The request has been sent
+ @retval other Some error occurs when sending the request.
+
+**/
+EFI_STATUS
+DhcpInitRequest (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot));
+
+ //
+ // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
+ //
+ DhcpSb->ActiveChild->ElaspedTime= 0;
+
+ if (DhcpSb->DhcpState == Dhcp4Init) {
+ DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE);
+ Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL);
+
+ if (EFI_ERROR (Status)) {
+ DhcpSb->DhcpState = Dhcp4Init;
+ return Status;
+ }
+ } else {
+ DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE);
+ Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL);
+
+ if (EFI_ERROR (Status)) {
+ DhcpSb->DhcpState = Dhcp4InitReboot;
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call user provided callback function, and return the value the
+ function returns. If the user doesn't provide a callback, a
+ proper return value is selected to let the caller continue the
+ normal process.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Event The event as defined in the spec
+ @param[in] Packet The current packet trigger the event
+ @param[out] NewPacket The user's return new packet
+
+ @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
+ @retval EFI_SUCCESS The user function returns success.
+ @retval EFI_ABORTED The user function ask it to abort.
+
+**/
+EFI_STATUS
+DhcpCallUser (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_EVENT Event,
+ IN EFI_DHCP4_PACKET *Packet, OPTIONAL
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ EFI_STATUS Status;
+
+ if (NewPacket != NULL) {
+ *NewPacket = NULL;
+ }
+
+ //
+ // If user doesn't provide the call back function, return the value
+ // that directs the client to continue the normal process.
+ // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
+ // the offers and select a offer, EFI_NOT_READY tells the client to
+ // collect more offers.
+ //
+ Config = &DhcpSb->ActiveConfig;
+
+ if (Config->Dhcp4Callback == NULL) {
+ if (Event == Dhcp4RcvdOffer) {
+ return EFI_NOT_READY;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ Status = Config->Dhcp4Callback (
+ &DhcpSb->ActiveChild->Dhcp4Protocol,
+ Config->CallbackContext,
+ (EFI_DHCP4_STATE) DhcpSb->DhcpState,
+ Event,
+ Packet,
+ NewPacket
+ );
+
+ //
+ // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
+ // and EFI_ABORTED. If it returns values other than those, assume
+ // it to be EFI_ABORTED.
+ //
+ if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) {
+ return Status;
+ }
+
+ return EFI_ABORTED;
+}
+
+
+/**
+ Notify the user about the operation result.
+
+ @param DhcpSb DHCP service instance
+ @param Which Which notify function to signal
+
+**/
+VOID
+DhcpNotifyUser (
+ IN DHCP_SERVICE *DhcpSb,
+ IN INTN Which
+ )
+{
+ DHCP_PROTOCOL *Child;
+
+ if ((Child = DhcpSb->ActiveChild) == NULL) {
+ return ;
+ }
+
+ if ((Child->CompletionEvent != NULL) &&
+ ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))
+ ) {
+
+ gBS->SignalEvent (Child->CompletionEvent);
+ Child->CompletionEvent = NULL;
+ }
+
+ if ((Child->RenewRebindEvent != NULL) &&
+ ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))
+ ) {
+
+ gBS->SignalEvent (Child->RenewRebindEvent);
+ Child->RenewRebindEvent = NULL;
+ }
+}
+
+
+
+/**
+ Set the DHCP state. If CallUser is true, it will try to notify
+ the user before change the state by DhcpNotifyUser. It returns
+ EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
+ EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
+ the return value of this function.
+
+ @param DhcpSb The DHCP service instance
+ @param State The new DHCP state to change to
+ @param CallUser Whether we need to call user
+
+ @retval EFI_SUCCESS The state is changed
+ @retval EFI_ABORTED The user asks to abort the DHCP process.
+
+**/
+EFI_STATUS
+DhcpSetState (
+ IN OUT DHCP_SERVICE *DhcpSb,
+ IN INTN State,
+ IN BOOLEAN CallUser
+ )
+{
+ EFI_STATUS Status;
+
+ if (CallUser) {
+ Status = EFI_SUCCESS;
+
+ if (State == Dhcp4Renewing) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL);
+
+ } else if (State == Dhcp4Rebinding) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL);
+
+ } else if (State == Dhcp4Bound) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL);
+
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Update the retransmission timer during the state transition.
+ // This will clear the retry count. This is also why the rule
+ // first transit the state, then send packets.
+ //
+ if (State == Dhcp4Selecting) {
+ DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount;
+ } else {
+ DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount;
+ }
+
+ if (DhcpSb->MaxRetries == 0) {
+ DhcpSb->MaxRetries = 4;
+ }
+
+ DhcpSb->CurRetry = 0;
+ DhcpSb->PacketToLive = 0;
+ DhcpSb->LastTimeout = 0;
+ DhcpSb->DhcpState = State;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Set the retransmit timer for the packet. It will select from either
+ the discover timeouts/request timeouts or the default timeout values.
+
+ @param DhcpSb The DHCP service instance.
+
+**/
+VOID
+DhcpSetTransmitTimer (
+ IN OUT DHCP_SERVICE *DhcpSb
+ )
+{
+ UINT32 *Times;
+
+ ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);
+
+ if (DhcpSb->DhcpState == Dhcp4Selecting) {
+ Times = DhcpSb->ActiveConfig.DiscoverTimeout;
+ } else {
+ Times = DhcpSb->ActiveConfig.RequestTimeout;
+ }
+
+ if (Times == NULL) {
+ Times = mDhcp4DefaultTimeout;
+ }
+
+ DhcpSb->PacketToLive = Times[DhcpSb->CurRetry];
+ DhcpSb->LastTimeout = DhcpSb->PacketToLive;
+
+ return;
+}
+
+/**
+ Compute the lease. If the server grants a permanent lease, just
+ process it as a normal timeout value since the lease will last
+ more than 100 years.
+
+ @param DhcpSb The DHCP service instance
+ @param Para The DHCP parameter extracted from the server's
+ response.
+**/
+VOID
+DhcpComputeLease (
+ IN OUT DHCP_SERVICE *DhcpSb,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ ASSERT (Para != NULL);
+
+ DhcpSb->Lease = Para->Lease;
+ DhcpSb->T2 = Para->T2;
+ DhcpSb->T1 = Para->T1;
+
+ if (DhcpSb->Lease == 0) {
+ DhcpSb->Lease = DHCP_DEFAULT_LEASE;
+ }
+
+ if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) {
+ DhcpSb->T2 = Para->Lease - (Para->Lease >> 3);
+ }
+
+ if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) {
+ DhcpSb->T1 = DhcpSb->Lease >> 1;
+ }
+}
+
+
+/**
+ Configure a UDP IO port to use the acquired lease address.
+ DHCP driver needs this port to unicast packet to the server
+ such as DHCP release.
+
+ @param[in] UdpIo The UDP IO to configure
+ @param[in] Context Dhcp service instance.
+
+ @retval EFI_SUCCESS The UDP IO port is successfully configured.
+ @retval Others It failed to configure the port.
+
+**/
+EFI_STATUS
+EFIAPI
+DhcpConfigLeaseIoPort (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ EFI_UDP4_CONFIG_DATA UdpConfigData;
+ EFI_IPv4_ADDRESS Subnet;
+ EFI_IPv4_ADDRESS Gateway;
+ DHCP_SERVICE *DhcpSb;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ DhcpSb = (DHCP_SERVICE *) Context;
+
+ UdpConfigData.AcceptBroadcast = FALSE;
+ UdpConfigData.AcceptPromiscuous = FALSE;
+ UdpConfigData.AcceptAnyPort = FALSE;
+ UdpConfigData.AllowDuplicatePort = TRUE;
+ UdpConfigData.TypeOfService = 0;
+ UdpConfigData.TimeToLive = 64;
+ UdpConfigData.DoNotFragment = FALSE;
+ UdpConfigData.ReceiveTimeout = 1;
+ UdpConfigData.TransmitTimeout = 0;
+
+ UdpConfigData.UseDefaultAddress = FALSE;
+ UdpConfigData.StationPort = DHCP_CLIENT_PORT;
+ UdpConfigData.RemotePort = DHCP_SERVER_PORT;
+
+ Ip = HTONL (DhcpSb->ClientAddr);
+ CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Netmask);
+ CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Add a default route if received from the server.
+ //
+ if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {
+ ZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));
+
+ Ip = HTONL (DhcpSb->Para->Router);
+ CopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ UdpIo->Protocol.Udp4->Routes (UdpIo->Protocol.Udp4, FALSE, &Subnet, &Subnet, &Gateway);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Update the lease states when a new lease is acquired. It will not only
+ save the acquired the address and lease time, it will also create a UDP
+ child to provide address resolution for the address.
+
+ @param DhcpSb The DHCP service instance
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_SUCCESS The lease is recorded.
+
+**/
+EFI_STATUS
+DhcpLeaseAcquired (
+ IN OUT DHCP_SERVICE *DhcpSb
+ )
+{
+ DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
+
+ if (DhcpSb->Para != NULL) {
+ DhcpSb->Netmask = DhcpSb->Para->NetMask;
+ DhcpSb->ServerAddr = DhcpSb->Para->ServerId;
+ }
+
+ if (DhcpSb->Netmask == 0) {
+ return EFI_ABORTED;
+ }
+
+ if (DhcpSb->LeaseIoPort != NULL) {
+ UdpIoFreeIo (DhcpSb->LeaseIoPort);
+ }
+
+ //
+ // Create a UDP/IP child to provide ARP service for the Leased IP,
+ // and transmit unicast packet with it as source address. Don't
+ // start receive on this port, the queued packet will be timeout.
+ //
+ DhcpSb->LeaseIoPort = UdpIoCreateIo (
+ DhcpSb->Controller,
+ DhcpSb->Image,
+ DhcpConfigLeaseIoPort,
+ UDP_IO_UDP4_VERSION,
+ DhcpSb
+ );
+
+ if (DhcpSb->LeaseIoPort == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
+ DhcpComputeLease (DhcpSb, DhcpSb->Para);
+ }
+
+ return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
+}
+
+
+/**
+ Clean up the DHCP related states, IoStatus isn't reset.
+
+ @param DhcpSb The DHCP instance service.
+
+**/
+VOID
+DhcpCleanLease (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ DhcpSb->DhcpState = Dhcp4Init;
+ DhcpSb->Xid = DhcpSb->Xid + 1;
+ DhcpSb->ClientAddr = 0;
+ DhcpSb->Netmask = 0;
+ DhcpSb->ServerAddr = 0;
+
+ if (DhcpSb->LastOffer != NULL) {
+ FreePool (DhcpSb->LastOffer);
+ DhcpSb->LastOffer = NULL;
+ }
+
+ if (DhcpSb->Selected != NULL) {
+ FreePool (DhcpSb->Selected);
+ DhcpSb->Selected = NULL;
+ }
+
+ if (DhcpSb->Para != NULL) {
+ FreePool (DhcpSb->Para);
+ DhcpSb->Para = NULL;
+ }
+
+ DhcpSb->Lease = 0;
+ DhcpSb->T1 = 0;
+ DhcpSb->T2 = 0;
+ DhcpSb->ExtraRefresh = FALSE;
+
+ if (DhcpSb->LeaseIoPort != NULL) {
+ UdpIoFreeIo (DhcpSb->LeaseIoPort);
+ DhcpSb->LeaseIoPort = NULL;
+ }
+
+ if (DhcpSb->LastPacket != NULL) {
+ FreePool (DhcpSb->LastPacket);
+ DhcpSb->LastPacket = NULL;
+ }
+
+ DhcpSb->PacketToLive = 0;
+ DhcpSb->LastTimeout = 0;
+ DhcpSb->CurRetry = 0;
+ DhcpSb->MaxRetries = 0;
+ DhcpSb->LeaseLife = 0;
+
+ //
+ // Clean active config data.
+ //
+ DhcpCleanConfigure (&DhcpSb->ActiveConfig);
+}
+
+
+/**
+ Select a offer among all the offers collected. If the offer selected is
+ of BOOTP, the lease is recorded and user notified. If the offer is of
+ DHCP, it will request the offer from the server.
+
+ @param[in] DhcpSb The DHCP service instance.
+
+ @retval EFI_SUCCESS One of the offer is selected.
+
+**/
+EFI_STATUS
+DhcpChooseOffer (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ EFI_DHCP4_PACKET *Selected;
+ EFI_DHCP4_PACKET *NewPacket;
+ EFI_DHCP4_PACKET *TempPacket;
+ EFI_STATUS Status;
+
+ ASSERT (DhcpSb->LastOffer != NULL);
+
+ //
+ // User will cache previous offers if he wants to select
+ // from multiple offers. If user provides an invalid packet,
+ // use the last offer, otherwise use the provided packet.
+ //
+ NewPacket = NULL;
+ Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Selected = DhcpSb->LastOffer;
+
+ if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
+ TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size);
+ if (TempPacket != NULL) {
+ CopyMem (TempPacket, NewPacket, NewPacket->Size);
+ FreePool (Selected);
+ Selected = TempPacket;
+ }
+ }
+
+ DhcpSb->Selected = Selected;
+ DhcpSb->LastOffer = NULL;
+ DhcpSb->Para = NULL;
+ DhcpValidateOptions (Selected, &DhcpSb->Para);
+
+ //
+ // A bootp offer has been selected, save the lease status,
+ // enter bound state then notify the user.
+ //
+ if (DHCP_IS_BOOTP (DhcpSb->Para)) {
+ Status = DhcpLeaseAcquired (DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Send a DHCP requests
+ //
+ Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
+}
+
+
+/**
+ Terminate the current address acquire. All the allocated resources
+ are released. Be careful when calling this function. A rule related
+ to this is: only call DhcpEndSession at the highest level, such as
+ DhcpInput, DhcpOnTimerTick...At the other level, just return error.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Status The result of the DHCP process.
+
+**/
+VOID
+DhcpEndSession (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_STATUS Status
+ )
+{
+ if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
+ DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
+ } else {
+ DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
+ }
+
+ DhcpCleanLease (DhcpSb);
+
+ DhcpSb->IoStatus = Status;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
+}
+
+
+/**
+ Handle packets in DHCP select state.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Packet The DHCP packet received
+ @param[in] Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occurred.
+
+**/
+EFI_STATUS
+DhcpHandleSelect (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // First validate the message:
+ // 1. the offer is a unicast
+ // 2. if it is a DHCP message, it must contains a server ID.
+ // Don't return a error for these two case otherwise the session is ended.
+ //
+ if (!DHCP_IS_BOOTP (Para) &&
+ ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))
+ ) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Call the user's callback. The action according to the return is as:
+ // 1. EFI_SUCCESS: stop waiting for more offers, select the offer now
+ // 2. EFI_NOT_READY: wait for more offers
+ // 3. EFI_ABORTED: abort the address acquiring.
+ //
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
+
+ if (Status == EFI_SUCCESS) {
+ if (DhcpSb->LastOffer != NULL) {
+ FreePool (DhcpSb->LastOffer);
+ }
+
+ DhcpSb->LastOffer = Packet;
+
+ return DhcpChooseOffer (DhcpSb);
+
+ } else if (Status == EFI_NOT_READY) {
+ if (DhcpSb->LastOffer != NULL) {
+ FreePool (DhcpSb->LastOffer);
+ }
+
+ DhcpSb->LastOffer = Packet;
+
+ } else if (Status == EFI_ABORTED) {
+ //
+ // DhcpInput will end the session upon error return. Remember
+ // only to call DhcpEndSession at the top level call.
+ //
+ goto ON_EXIT;
+ }
+
+ return EFI_SUCCESS;
+
+ON_EXIT:
+ FreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP request state.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Packet The DHCP packet received
+ @param[in] Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occurred.
+
+**/
+EFI_STATUS
+DhcpHandleRequest (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *Selected;
+ EFI_STATUS Status;
+ UINT8 *Message;
+
+ ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
+
+ Head = &Packet->Dhcp4.Header;
+ Selected = &DhcpSb->Selected->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ (Para->ServerId != DhcpSb->Para->ServerId) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
+ ) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Received a NAK, end the session no matter what the user returns
+ //
+ Status = EFI_DEVICE_ERROR;
+
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the ACK matches the selected offer
+ //
+ Message = NULL;
+
+ if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
+ Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";
+ goto REJECT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+
+ if (EFI_ERROR (Status)) {
+ Message = (UINT8 *) "Lease is denied upon received ACK";
+ goto REJECT;
+ }
+
+ //
+ // Record the lease, transit to BOUND state, then notify the user
+ //
+ Status = DhcpLeaseAcquired (DhcpSb);
+
+ if (EFI_ERROR (Status)) {
+ Message = (UINT8 *) "Lease is denied upon entering bound";
+ goto REJECT;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
+
+ FreePool (Packet);
+ return EFI_SUCCESS;
+
+REJECT:
+ DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
+
+ON_EXIT:
+ FreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP renew/rebound state.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Packet The DHCP packet received
+ @param[in] Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occurred.
+
+**/
+EFI_STATUS
+DhcpHandleRenewRebind (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *Selected;
+ EFI_STATUS Status;
+
+ ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
+
+ Head = &Packet->Dhcp4.Header;
+ Selected = &DhcpSb->Selected->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ (Para->ServerId != DhcpSb->Para->ServerId) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
+ ) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Received a NAK, ignore the user's return then terminate the process
+ //
+ Status = EFI_DEVICE_ERROR;
+
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // The lease is different from the selected. Don't send a DECLINE
+ // since it isn't existed in the client's FSM.
+ //
+ if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
+ goto ON_EXIT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Record the lease, start timer for T1 and T2,
+ //
+ DhcpComputeLease (DhcpSb, Para);
+ DhcpSb->LeaseLife = 0;
+ DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
+
+ if (DhcpSb->ExtraRefresh != 0) {
+ DhcpSb->ExtraRefresh = FALSE;
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
+ }
+
+ON_EXIT:
+ FreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle packets in DHCP reboot state.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Packet The DHCP packet received
+ @param[in] Para The DHCP parameter extracted from the packet. That
+ is, all the option value that we care.
+
+ @retval EFI_SUCCESS The packet is successfully processed.
+ @retval Others Some error occurred.
+
+**/
+EFI_STATUS
+DhcpHandleReboot (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_PARAMETER *Para
+ )
+{
+ EFI_DHCP4_HEADER *Head;
+ EFI_STATUS Status;
+
+ Head = &Packet->Dhcp4.Header;
+
+ //
+ // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
+ //
+ if (DHCP_IS_BOOTP (Para) ||
+ ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
+ ) {
+
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // If a NAK is received, transit to INIT and try again.
+ //
+ if (Para->DhcpType == DHCP_MSG_NAK) {
+ DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
+
+ DhcpSb->ClientAddr = 0;
+ DhcpSb->DhcpState = Dhcp4Init;
+
+ Status = DhcpInitRequest (DhcpSb);
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the ACK matches the selected offer
+ //
+ if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // OK, get the parameter from server, record the lease
+ //
+ DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);
+ if (DhcpSb->Para == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ DhcpSb->Selected = Packet;
+ Status = DhcpLeaseAcquired (DhcpSb);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DhcpSb->IoStatus = EFI_SUCCESS;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
+ return EFI_SUCCESS;
+
+ON_EXIT:
+ FreePool (Packet);
+ return Status;
+}
+
+
+/**
+ Handle the received DHCP packets. This function drives the DHCP
+ state machine.
+
+ @param UdpPacket The UDP packets received.
+ @param EndPoint The local/remote UDP access point
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+DhcpInput (
+ NET_BUF *UdpPacket,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ DHCP_SERVICE *DhcpSb;
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_PACKET *Packet;
+ DHCP_PARAMETER *Para;
+ EFI_STATUS Status;
+ UINT32 Len;
+
+ Packet = NULL;
+ DhcpSb = (DHCP_SERVICE *) Context;
+
+ //
+ // Don't restart receive if error occurs or DHCP is destroyed.
+ //
+ if (EFI_ERROR (IoStatus)) {
+ return ;
+ } else if (DhcpSb->ServiceState == DHCP_DESTROY) {
+ NetbufFree (UdpPacket);
+ return ;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (DhcpSb->DhcpState == Dhcp4Stopped) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the packet received
+ //
+ if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
+ goto RESTART;
+ }
+
+ //
+ // Copy the DHCP message to a continuous memory block
+ //
+ Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
+ Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len);
+
+ if (Packet == NULL) {
+ goto RESTART;
+ }
+
+ Packet->Size = Len;
+ Head = &Packet->Dhcp4.Header;
+ Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
+
+ if (Packet->Length != UdpPacket->TotalSize) {
+ goto RESTART;
+ }
+
+ //
+ // Is this packet the answer to our packet?
+ //
+ if ((Head->OpCode != BOOTP_REPLY) ||
+ (NTOHL (Head->Xid) != DhcpSb->Xid) ||
+ (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
+ goto RESTART;
+ }
+
+ //
+ // Validate the options and retrieve the interested options
+ //
+ Para = NULL;
+ if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
+ (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
+ EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {
+
+ goto RESTART;
+ }
+
+ //
+ // Call the handler for each state. The handler should return
+ // EFI_SUCCESS if the process can go on no matter whether the
+ // packet is ignored or not. If the return is EFI_ERROR, the
+ // session will be terminated. Packet's ownership is handled
+ // over to the handlers. If operation succeeds, the handler
+ // must notify the user. It isn't necessary to do if EFI_ERROR
+ // is returned because the DhcpEndSession will notify the user.
+ //
+ Status = EFI_SUCCESS;
+
+ switch (DhcpSb->DhcpState) {
+ case Dhcp4Selecting:
+ Status = DhcpHandleSelect (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4Requesting:
+ Status = DhcpHandleRequest (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4InitReboot:
+ case Dhcp4Init:
+ case Dhcp4Bound:
+ //
+ // Ignore the packet in INITREBOOT, INIT and BOUND states
+ //
+ FreePool (Packet);
+ Status = EFI_SUCCESS;
+ break;
+
+ case Dhcp4Renewing:
+ case Dhcp4Rebinding:
+ Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
+ break;
+
+ case Dhcp4Rebooting:
+ Status = DhcpHandleReboot (DhcpSb, Packet, Para);
+ break;
+ }
+
+ if (Para != NULL) {
+ FreePool (Para);
+ }
+
+ Packet = NULL;
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (UdpPacket);
+ UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
+ DhcpEndSession (DhcpSb, Status);
+ return ;
+ }
+
+RESTART:
+ NetbufFree (UdpPacket);
+
+ if (Packet != NULL) {
+ FreePool (Packet);
+ }
+
+ Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
+
+ if (EFI_ERROR (Status)) {
+ DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
+ }
+}
+
+/**
+ Release the net buffer when packet is sent.
+
+ @param UdpPacket The UDP packets received.
+ @param EndPoint The local/remote UDP access point
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+DhcpOnPacketSent (
+ NET_BUF *Packet,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ )
+{
+ NetbufFree (Packet);
+}
+
+
+
+/**
+ Build and transmit a DHCP message according to the current states.
+ This function implement the Table 5. of RFC 2131. Always transits
+ the state (as defined in Figure 5. of the same RFC) before sending
+ a DHCP message. The table is adjusted accordingly.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Seed The seed packet which the new packet is based on
+ @param[in] Para The DHCP parameter of the Seed packet
+ @param[in] Type The message type to send
+ @param[in] Msg The human readable message to include in the packet
+ sent.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
+ @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
+ @retval EFI_SUCCESS The message is sent
+ @retval other Other error occurs
+
+**/
+EFI_STATUS
+DhcpSendMessage (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Seed,
+ IN DHCP_PARAMETER *Para,
+ IN UINT8 Type,
+ IN UINT8 *Msg
+ )
+{
+ EFI_DHCP4_CONFIG_DATA *Config;
+ EFI_DHCP4_PACKET *Packet;
+ EFI_DHCP4_PACKET *NewPacket;
+ EFI_DHCP4_HEADER *Head;
+ EFI_DHCP4_HEADER *SeedHead;
+ UDP_IO *UdpIo;
+ UDP_END_POINT EndPoint;
+ NET_BUF *Wrap;
+ NET_FRAGMENT Frag;
+ EFI_STATUS Status;
+ IP4_ADDR IpAddr;
+ UINT8 *Buf;
+ UINT16 MaxMsg;
+ UINT32 Len;
+ UINT32 Index;
+
+ //
+ // Allocate a big enough memory block to hold the DHCP packet
+ //
+ Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
+
+ if (Msg != NULL) {
+ Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);
+ }
+
+ Packet = AllocatePool (Len);
+
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = Len;
+ Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
+
+ //
+ // Fill in the DHCP header fields
+ //
+ Config = &DhcpSb->ActiveConfig;
+ SeedHead = NULL;
+
+ if (Seed != NULL) {
+ SeedHead = &Seed->Dhcp4.Header;
+ }
+
+ Head = &Packet->Dhcp4.Header;
+ ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
+
+ Head->OpCode = BOOTP_REQUEST;
+ Head->HwType = DhcpSb->HwType;
+ Head->HwAddrLen = DhcpSb->HwLen;
+ Head->Xid = HTONL (DhcpSb->Xid);
+ Head->Reserved = HTONS (0x8000); //Server, broadcast the message please.
+
+ EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
+ CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
+
+ if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) {
+ Head->Seconds = 0;
+ } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) {
+ //
+ // Use the same value as the original DHCPDISCOVER message.
+ //
+ Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds;
+ } else {
+ SetElapsedTime(&Head->Seconds, DhcpSb->ActiveChild);
+ }
+
+ //
+ // Append the DHCP message type
+ //
+ Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
+ Buf = Packet->Dhcp4.Option;
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_MSG_TYPE, 1, &Type);
+
+ //
+ // Append the serverid option if necessary:
+ // 1. DHCP decline message
+ // 2. DHCP release message
+ // 3. DHCP request to confirm one lease.
+ //
+ if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
+ ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))
+ ) {
+
+ ASSERT ((Para != NULL) && (Para->ServerId != 0));
+
+ IpAddr = HTONL (Para->ServerId);
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);
+ }
+
+ //
+ // Append the requested IP option if necessary:
+ // 1. DHCP request to use the previously allocated address
+ // 2. DHCP request to confirm one lease
+ // 3. DHCP decline to decline one lease
+ //
+ IpAddr = 0;
+
+ if (Type == DHCP_MSG_REQUEST) {
+ if (DhcpSb->DhcpState == Dhcp4Rebooting) {
+ IpAddr = EFI_IP4 (Config->ClientAddress);
+
+ } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
+ ASSERT (SeedHead != NULL);
+ IpAddr = EFI_IP4 (SeedHead->YourAddr);
+ }
+
+ } else if (Type == DHCP_MSG_DECLINE) {
+ ASSERT (SeedHead != NULL);
+ IpAddr = EFI_IP4 (SeedHead->YourAddr);
+ }
+
+ if (IpAddr != 0) {
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);
+ }
+
+ //
+ // Append the Max Message Length option if it isn't a DECLINE
+ // or RELEASE to direct the server use large messages instead of
+ // override the BOOTFILE and SERVER fields in the message head.
+ //
+ if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
+ MaxMsg = HTONS (0xFF00);
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);
+ }
+
+ //
+ // Append the user's message if it isn't NULL
+ //
+ if (Msg != NULL) {
+ Len = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);
+ Buf = DhcpAppendOption (Buf, DHCP4_TAG_MESSAGE, (UINT16) Len, Msg);
+ }
+
+ //
+ // Append the user configured options
+ //
+ if (DhcpSb->UserOptionLen != 0) {
+ for (Index = 0; Index < Config->OptionCount; Index++) {
+ //
+ // We can't use any option other than the client ID from user
+ // if it is a DHCP decline or DHCP release .
+ //
+ if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
+ (Config->OptionList[Index]->OpCode != DHCP4_TAG_CLIENT_ID)) {
+ continue;
+ }
+
+ Buf = DhcpAppendOption (
+ Buf,
+ Config->OptionList[Index]->OpCode,
+ Config->OptionList[Index]->Length,
+ Config->OptionList[Index]->Data
+ );
+ }
+ }
+
+ *(Buf++) = DHCP4_TAG_EOP;
+ Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);
+
+ //
+ // OK, the message is built, call the user to override it.
+ //
+ Status = EFI_SUCCESS;
+ NewPacket = NULL;
+
+ if (Type == DHCP_MSG_DISCOVER) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
+
+ } else if (Type == DHCP_MSG_REQUEST) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
+
+ } else if (Type == DHCP_MSG_DECLINE) {
+ Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
+ }
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ if (NewPacket != NULL) {
+ FreePool (Packet);
+ Packet = NewPacket;
+ }
+
+ //
+ // Save the Client Address will be sent out
+ //
+ CopyMem (
+ &DhcpSb->ClientAddressSendOut[0],
+ &Packet->Dhcp4.Header.ClientHwAddr[0],
+ Packet->Dhcp4.Header.HwAddrLen
+ );
+
+ //
+ // Wrap it into a netbuf then send it.
+ //
+ Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;
+ Frag.Len = Packet->Length;
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
+
+ if (Wrap == NULL) {
+ FreePool (Packet);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Save it as the last sent packet for retransmission
+ //
+ if (DhcpSb->LastPacket != NULL) {
+ FreePool (DhcpSb->LastPacket);
+ }
+
+ DhcpSb->LastPacket = Packet;
+ DhcpSetTransmitTimer (DhcpSb);
+
+ //
+ // Broadcast the message, unless we know the server address.
+ // Use the lease UdpIo port to send the unicast packet.
+ //
+ EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
+ EndPoint.LocalAddr.Addr[0] = 0;
+ EndPoint.RemotePort = DHCP_SERVER_PORT;
+ EndPoint.LocalPort = DHCP_CLIENT_PORT;
+ UdpIo = DhcpSb->UdpIo;
+
+ if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
+ EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
+ EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;
+ UdpIo = DhcpSb->LeaseIoPort;
+ }
+
+ ASSERT (UdpIo != NULL);
+
+ Status = UdpIoSendDatagram (
+ UdpIo,
+ Wrap,
+ &EndPoint,
+ NULL,
+ DhcpOnPacketSent,
+ DhcpSb
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Wrap);
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retransmit a saved packet. Only DISCOVER and REQUEST messages
+ will be retransmitted.
+
+ @param[in] DhcpSb The DHCP service instance
+
+ @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
+ @retval EFI_SUCCESS The packet is retransmitted.
+
+**/
+EFI_STATUS
+DhcpRetransmit (
+ IN DHCP_SERVICE *DhcpSb
+ )
+{
+ UDP_IO *UdpIo;
+ UDP_END_POINT EndPoint;
+ NET_BUF *Wrap;
+ NET_FRAGMENT Frag;
+ EFI_STATUS Status;
+
+ ASSERT (DhcpSb->LastPacket != NULL);
+
+ //
+ // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
+ //
+ if (DhcpSb->DhcpState != Dhcp4Requesting) {
+ SetElapsedTime(&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild);
+ }
+
+ //
+ // Wrap it into a netbuf then send it.
+ //
+ Frag.Bulk = (UINT8 *) &DhcpSb->LastPacket->Dhcp4.Header;
+ Frag.Len = DhcpSb->LastPacket->Length;
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
+
+ if (Wrap == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Broadcast the message, unless we know the server address.
+ //
+ EndPoint.RemotePort = DHCP_SERVER_PORT;
+ EndPoint.LocalPort = DHCP_CLIENT_PORT;
+ EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
+ EndPoint.LocalAddr.Addr[0] = 0;
+ UdpIo = DhcpSb->UdpIo;
+
+ if (DhcpSb->DhcpState == Dhcp4Renewing) {
+ EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
+ EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;
+ UdpIo = DhcpSb->LeaseIoPort;
+ }
+
+ ASSERT (UdpIo != NULL);
+
+ Status = UdpIoSendDatagram (
+ UdpIo,
+ Wrap,
+ &EndPoint,
+ NULL,
+ DhcpOnPacketSent,
+ DhcpSb
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Wrap);
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Each DHCP service has three timer. Two of them are count down timer.
+ One for the packet retransmission. The other is to collect the offers.
+ The third timer increments the lease life which is compared to T1, T2,
+ and lease to determine the time to renew and rebind the lease.
+ DhcpOnTimerTick will be called once every second.
+
+ @param[in] Event The timer event
+ @param[in] Context The context, which is the DHCP service instance.
+
+**/
+VOID
+EFIAPI
+DhcpOnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ DHCP_SERVICE *DhcpSb;
+ DHCP_PROTOCOL *Instance;
+ EFI_STATUS Status;
+
+ DhcpSb = (DHCP_SERVICE *) Context;
+ Instance = DhcpSb->ActiveChild;
+
+ //
+ // 0xffff is the maximum supported value for elapsed time according to RFC.
+ //
+ if (Instance != NULL && Instance->ElaspedTime < 0xffff) {
+ Instance->ElaspedTime++;
+ }
+
+ //
+ // Check the retransmit timer
+ //
+ if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
+
+ //
+ // Select offer at each timeout if any offer received.
+ //
+ if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) {
+
+ Status = DhcpChooseOffer (DhcpSb);
+
+ if (EFI_ERROR(Status)) {
+ if (DhcpSb->LastOffer != NULL) {
+ FreePool (DhcpSb->LastOffer);
+ DhcpSb->LastOffer = NULL;
+ }
+ } else {
+ goto ON_EXIT;
+ }
+ }
+
+ if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
+ //
+ // Still has another try
+ //
+ DhcpRetransmit (DhcpSb);
+ DhcpSetTransmitTimer (DhcpSb);
+
+ } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
+
+ //
+ // Retransmission failed, if the DHCP request is initiated by
+ // user, adjust the current state according to the lease life.
+ // Otherwise do nothing to wait the lease to timeout
+ //
+ if (DhcpSb->ExtraRefresh != 0) {
+ Status = EFI_SUCCESS;
+
+ if (DhcpSb->LeaseLife < DhcpSb->T1) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
+
+ } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
+
+ } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
+ Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
+
+ } else {
+ goto END_SESSION;
+
+ }
+
+ DhcpSb->IoStatus = EFI_TIMEOUT;
+ DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
+ }
+ } else {
+ goto END_SESSION;
+ }
+ }
+
+ //
+ // If an address has been acquired, check whether need to
+ // refresh or whether it has expired.
+ //
+ if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
+ DhcpSb->LeaseLife++;
+
+ //
+ // Don't timeout the lease, only count the life if user is
+ // requesting extra renew/rebind. Adjust the state after that.
+ //
+ if (DhcpSb->ExtraRefresh != 0) {
+ return ;
+ }
+
+ if (DhcpSb->LeaseLife == DhcpSb->Lease) {
+ //
+ // Lease expires, end the session
+ //
+ goto END_SESSION;
+
+ } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
+ //
+ // T2 expires, transit to rebinding then send a REQUEST to any server
+ //
+ if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
+ goto END_SESSION;
+ }
+
+ if (Instance != NULL) {
+ Instance->ElaspedTime= 0;
+ }
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto END_SESSION;
+ }
+
+ } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
+ //
+ // T1 expires, transit to renewing, then send a REQUEST to the server
+ //
+ if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
+ goto END_SESSION;
+ }
+
+ if (Instance != NULL) {
+ Instance->ElaspedTime= 0;
+ }
+
+ Status = DhcpSendMessage (
+ DhcpSb,
+ DhcpSb->Selected,
+ DhcpSb->Para,
+ DHCP_MSG_REQUEST,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto END_SESSION;
+ }
+ }
+ }
+
+ON_EXIT:
+ //
+ // Iterate through all the DhcpSb Children.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &DhcpSb->Children) {
+ Instance = NET_LIST_USER_STRUCT (Entry, DHCP_PROTOCOL, Link);
+ Instance->Timeout--;
+ if (Instance->Timeout == 0 && Instance->Token != NULL) {
+ PxeDhcpDone (Instance);
+ }
+ }
+
+ return ;
+
+END_SESSION:
+ DhcpEndSession (DhcpSb, EFI_TIMEOUT);
+
+ return ;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Io.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Io.h
new file mode 100644
index 00000000..d23003f8
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Io.h
@@ -0,0 +1,189 @@
+/** @file
+ The DHCP4 protocol implementation.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_DHCP4_IO_H__
+#define __EFI_DHCP4_IO_H__
+
+#include <Uefi.h>
+
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/NetLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+
+
+#define DHCP_WAIT_OFFER 3 // Time to wait the offers
+#define DHCP_DEFAULT_LEASE 7 * 24 * 60 * 60 // Seven days as default.
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+//
+// BOOTP header "op" field
+//
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+//
+// DHCP message types
+//
+#define DHCP_MSG_DISCOVER 1
+#define DHCP_MSG_OFFER 2
+#define DHCP_MSG_REQUEST 3
+#define DHCP_MSG_DECLINE 4
+#define DHCP_MSG_ACK 5
+#define DHCP_MSG_NAK 6
+#define DHCP_MSG_RELEASE 7
+#define DHCP_MSG_INFORM 8
+
+//
+// DHCP notify user type
+//
+#define DHCP_NOTIFY_COMPLETION 1
+#define DHCP_NOTIFY_RENEWREBIND 2
+#define DHCP_NOTIFY_ALL 3
+
+#define DHCP_IS_BOOTP(Parameter) (((Parameter) == NULL) || ((Parameter)->DhcpType == 0))
+
+#define DHCP_CONNECTED(State) \
+ (((State) == Dhcp4Bound) || ((State) == (Dhcp4Renewing)) || ((State) == Dhcp4Rebinding))
+
+/**
+ Set the DHCP state. If CallUser is true, it will try to notify
+ the user before change the state by DhcpNotifyUser. It returns
+ EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
+ EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
+ the return value of this function.
+
+ @param DhcpSb The DHCP service instance
+ @param State The new DHCP state to change to
+ @param CallUser Whether we need to call user
+
+ @retval EFI_SUCCESS The state is changed
+ @retval EFI_ABORTED The user asks to abort the DHCP process.
+
+**/
+EFI_STATUS
+DhcpSetState (
+ IN OUT DHCP_SERVICE *DhcpSb,
+ IN INTN State,
+ IN BOOLEAN CallUser
+ );
+
+/**
+ Build and transmit a DHCP message according to the current states.
+ This function implement the Table 5. of RFC 2131. Always transits
+ the state (as defined in Figure 5. of the same RFC) before sending
+ a DHCP message. The table is adjusted accordingly.
+
+ @param[in] DhcpSb The DHCP service instance
+ @param[in] Seed The seed packet which the new packet is based on
+ @param[in] Para The DHCP parameter of the Seed packet
+ @param[in] Type The message type to send
+ @param[in] Msg The human readable message to include in the packet
+ sent.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
+ @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
+ @retval EFI_SUCCESS The message is sent
+ @retval other Other error occurs
+
+**/
+EFI_STATUS
+DhcpSendMessage (
+ IN DHCP_SERVICE *DhcpSb,
+ IN EFI_DHCP4_PACKET *Seed,
+ IN DHCP_PARAMETER *Para,
+ IN UINT8 Type,
+ IN UINT8 *Msg
+ );
+
+/**
+ Each DHCP service has three timer. Two of them are count down timer.
+ One for the packet retransmission. The other is to collect the offers.
+ The third timer increments the lease life which is compared to T1, T2,
+ and lease to determine the time to renew and rebind the lease.
+ DhcpOnTimerTick will be called once every second.
+
+ @param[in] Event The timer event
+ @param[in] Context The context, which is the DHCP service instance.
+
+**/
+VOID
+EFIAPI
+DhcpOnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Handle the received DHCP packets. This function drives the DHCP
+ state machine.
+
+ @param UdpPacket The UDP packets received.
+ @param EndPoint The local/remote UDP access point
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+DhcpInput (
+ NET_BUF *UdpPacket,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ );
+
+/**
+ Send an initial DISCOVER or REQUEST message according to the
+ DHCP service's current state.
+
+ @param[in] DhcpSb The DHCP service instance
+
+ @retval EFI_SUCCESS The request has been sent
+ @retval other Some error occurs when sending the request.
+
+**/
+EFI_STATUS
+DhcpInitRequest (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+/**
+ Clean up the DHCP related states, IoStatus isn't reset.
+
+ @param DhcpSb The DHCP instance service.
+
+**/
+VOID
+DhcpCleanLease (
+ IN DHCP_SERVICE *DhcpSb
+ );
+
+/**
+ Release the net buffer when packet is sent.
+
+ @param UdpPacket The UDP packets received.
+ @param EndPoint The local/remote UDP access point
+ @param IoStatus The status of the UDP receive
+ @param Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+DhcpOnPacketSent (
+ NET_BUF *Packet,
+ UDP_END_POINT *EndPoint,
+ EFI_STATUS IoStatus,
+ VOID *Context
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Option.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Option.c
new file mode 100644
index 00000000..78402b3c
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Option.c
@@ -0,0 +1,890 @@
+/** @file
+ Function to validate, parse, process the DHCP options.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Dhcp4Impl.h"
+
+///
+/// A list of the format of DHCP Options sorted by option tag
+/// to validate a dhcp message. Refere the comments of the
+/// DHCP_OPTION_FORMAT structure.
+///
+DHCP_OPTION_FORMAT DhcpOptionFormats[] = {
+ {DHCP4_TAG_NETMASK, DHCP_OPTION_IP, 1, 1 , TRUE},
+ {DHCP4_TAG_TIME_OFFSET, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP4_TAG_ROUTER, DHCP_OPTION_IP, 1, -1 , TRUE},
+ {DHCP4_TAG_TIME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NAME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_DNS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_LOG_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_COOKIE_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_LPR_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_IMPRESS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_RL_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_HOSTNAME, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_BOOTFILE_LEN, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP4_TAG_DUMP, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_DOMAINNAME, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_SWAP_SERVER, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP4_TAG_ROOTPATH, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_EXTEND_PATH, DHCP_OPTION_INT8, 1, -1 , FALSE},
+
+ {DHCP4_TAG_IPFORWARD, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_NONLOCAL_SRR, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_POLICY_SRR, DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
+ {DHCP4_TAG_EMTU, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP4_TAG_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP4_TAG_PATHMTU_AGE, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP4_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16, 1, -1 , FALSE},
+
+ {DHCP4_TAG_IFMTU, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP4_TAG_SUBNET_LOCAL, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_BROADCAST, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP4_TAG_DISCOVER_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_SUPPLY_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP4_TAG_STATIC_ROUTE, DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
+
+ {DHCP4_TAG_TRAILER, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+ {DHCP4_TAG_ARPAGE, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP4_TAG_ETHER_ENCAP, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+
+ {DHCP4_TAG_TCP_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP4_TAG_KEEP_INTERVAL, DHCP_OPTION_INT32, 1, 1 , FALSE},
+ {DHCP4_TAG_KEEP_GARBAGE, DHCP_OPTION_SWITCH, 1, 1 , FALSE},
+
+ {DHCP4_TAG_NIS_DOMAIN, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_NIS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NTP_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_VENDOR, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_NBNS, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NBDD, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NBTYPE, DHCP_OPTION_INT8, 1, 1 , FALSE},
+ {DHCP4_TAG_NBSCOPE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_XFONT, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_XDM, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP4_TAG_REQUEST_IP, DHCP_OPTION_IP, 1, 1 , FALSE},
+ {DHCP4_TAG_LEASE, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP4_TAG_OVERLOAD, DHCP_OPTION_INT8, 1, 1 , TRUE},
+ {DHCP4_TAG_MSG_TYPE, DHCP_OPTION_INT8, 1, 1 , TRUE},
+ {DHCP4_TAG_SERVER_ID, DHCP_OPTION_IP, 1, 1 , TRUE},
+ {DHCP4_TAG_PARA_LIST, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_MESSAGE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_MAXMSG, DHCP_OPTION_INT16, 1, 1 , FALSE},
+ {DHCP4_TAG_T1, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP4_TAG_T2, DHCP_OPTION_INT32, 1, 1 , TRUE},
+ {DHCP4_TAG_VENDOR_CLASS_ID,DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_CLIENT_ID, DHCP_OPTION_INT8, 2, -1 , FALSE},
+
+ {DHCP4_TAG_NISPLUS, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_NISPLUS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP4_TAG_TFTP, DHCP_OPTION_INT8, 1, -1 , FALSE},
+ {DHCP4_TAG_BOOTFILE, DHCP_OPTION_INT8, 1, -1 , FALSE},
+
+ {DHCP4_TAG_MOBILEIP, DHCP_OPTION_IP, 0, -1 , FALSE},
+ {DHCP4_TAG_SMTP, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_POP3, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_NNTP, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_WWW, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_FINGER, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_IRC, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_STTALK, DHCP_OPTION_IP, 1, -1 , FALSE},
+ {DHCP4_TAG_STDA, DHCP_OPTION_IP, 1, -1 , FALSE},
+
+ {DHCP4_TAG_CLASSLESS_ROUTE,DHCP_OPTION_INT8, 5, -1 , FALSE},
+};
+
+
+/**
+ Binary search the DhcpOptionFormats array to find the format
+ information about a specific option.
+
+ @param[in] Tag The option's tag.
+
+ @return The point to the option's format, NULL if not found.
+
+**/
+DHCP_OPTION_FORMAT *
+DhcpFindOptionFormat (
+ IN UINT8 Tag
+ )
+{
+ INTN Left;
+ INTN Right;
+ INTN Middle;
+
+ Left = 0;
+ Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1;
+
+ while (Right >= Left) {
+ Middle = (Left + Right) / 2;
+
+ if (Tag == DhcpOptionFormats[Middle].Tag) {
+ return &DhcpOptionFormats[Middle];
+ }
+
+ if (Tag < DhcpOptionFormats[Middle].Tag) {
+ Right = Middle - 1;
+ } else {
+ Left = Middle + 1;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Validate whether a single DHCP option is valid according to its format.
+
+ @param[in] Format The option's format
+ @param[in] OptValue The value of the option
+ @param[in] Len The length of the option value
+
+ @retval TRUE The option is valid.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+DhcpOptionIsValid (
+ IN DHCP_OPTION_FORMAT *Format,
+ IN UINT8 *OptValue,
+ IN INTN Len
+ )
+{
+ INTN Unit;
+ INTN Occur;
+ INTN Index;
+
+ Unit = 0;
+
+ switch (Format->Type) {
+ case DHCP_OPTION_SWITCH:
+ case DHCP_OPTION_INT8:
+ Unit = 1;
+ break;
+
+ case DHCP_OPTION_INT16:
+ Unit = 2;
+ break;
+
+ case DHCP_OPTION_INT32:
+ case DHCP_OPTION_IP:
+ Unit = 4;
+ break;
+
+ case DHCP_OPTION_IPPAIR:
+ Unit = 8;
+ break;
+ }
+
+ ASSERT (Unit != 0);
+
+ //
+ // Validate that the option appears in the full units.
+ //
+ if ((Len % Unit) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Validate the occurrence of the option unit is with in [MinOccur, MaxOccur]
+ //
+ Occur = Len / Unit;
+
+ if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) ||
+ ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))
+ ) {
+ return FALSE;
+ }
+
+ //
+ // If the option is of type switch, only 0/1 are valid values.
+ //
+ if (Format->Type == DHCP_OPTION_SWITCH) {
+ for (Index = 0; Index < Occur; Index++) {
+ if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Extract the client interested options, all the parameters are
+ converted to host byte order.
+
+ @param[in] Tag The DHCP option tag
+ @param[in] Len The length of the option
+ @param[in] Data The value of the DHCP option
+ @param[out] Para The variable to save the interested parameter
+
+ @retval EFI_SUCCESS The DHCP option is successfully extracted.
+ @retval EFI_INVALID_PARAMETER The DHCP option is mal-formatted
+
+**/
+EFI_STATUS
+DhcpGetParameter (
+ IN UINT8 Tag,
+ IN INTN Len,
+ IN UINT8 *Data,
+ OUT DHCP_PARAMETER *Para
+ )
+{
+ switch (Tag) {
+ case DHCP4_TAG_NETMASK:
+ Para->NetMask = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_ROUTER:
+ //
+ // Return the first router to consumer which is the preferred one
+ //
+ Para->Router = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_LEASE:
+ Para->Lease = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_OVERLOAD:
+ Para->Overload = *Data;
+
+ if ((Para->Overload < 1) || (Para->Overload > 3)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case DHCP4_TAG_MSG_TYPE:
+ Para->DhcpType = *Data;
+
+ if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+
+ case DHCP4_TAG_SERVER_ID:
+ Para->ServerId = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_T1:
+ Para->T1 = NetGetUint32 (Data);
+ break;
+
+ case DHCP4_TAG_T2:
+ Para->T2 = NetGetUint32 (Data);
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Inspect all the options in a single buffer. DHCP options may be contained
+ in several buffers, such as the BOOTP options filed, boot file or server
+ name. Each option buffer is required to end with DHCP4_TAG_EOP.
+
+ @param[in] Buffer The buffer which contains DHCP options
+ @param[in] BufLen The length of the buffer
+ @param[in] Check The callback function for each option found
+ @param[in] Context The opaque parameter for the Check
+ @param[out] Overload Variable to save the value of DHCP4_TAG_OVERLOAD
+ option.
+
+ @retval EFI_SUCCESS All the options are valid
+ @retval EFI_INVALID_PARAMETER The options are mal-formatted.
+
+**/
+EFI_STATUS
+DhcpIterateBufferOptions (
+ IN UINT8 *Buffer,
+ IN INTN BufLen,
+ IN DHCP_CHECK_OPTION Check OPTIONAL,
+ IN VOID *Context,
+ OUT UINT8 *Overload OPTIONAL
+ )
+{
+ INTN Cur;
+ UINT8 Tag;
+ UINT8 Len;
+
+ Cur = 0;
+
+ while (Cur < BufLen) {
+ Tag = Buffer[Cur];
+
+ if (Tag == DHCP4_TAG_PAD) {
+ Cur++;
+ continue;
+ } else if (Tag == DHCP4_TAG_EOP) {
+ return EFI_SUCCESS;
+ }
+
+ Cur++;
+
+ if (Cur == BufLen) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Len = Buffer[Cur++];
+
+ if (Cur + Len > BufLen) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Tag == DHCP4_TAG_OVERLOAD) && (Overload != NULL)) {
+ if (Len != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Overload = Buffer[Cur];
+ }
+
+ if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cur += Len;
+ }
+
+ //
+ // Each option buffer is expected to end with an EOP
+ //
+ return EFI_INVALID_PARAMETER;
+}
+
+
+/**
+ Iterate through a DHCP message to visit each option. First inspect
+ all the options in the OPTION field. Then if overloaded, inspect
+ the options in FILENAME and SERVERNAME fields. One option may be
+ encoded in several places. See RFC 3396 Encoding Long Options in DHCP
+
+ @param[in] Packet The DHCP packet to check the options for
+ @param[in] Check The callback function to be called for each option
+ found
+ @param[in] Context The opaque parameter for Check
+
+ @retval EFI_SUCCESS The DHCP packet's options are well formatted
+ @retval EFI_INVALID_PARAMETER The DHCP packet's options are not well formatted
+
+**/
+EFI_STATUS
+DhcpIterateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_CHECK_OPTION Check OPTIONAL,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Overload;
+
+ Overload = 0;
+
+ Status = DhcpIterateBufferOptions (
+ Packet->Dhcp4.Option,
+ Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32),
+ Check,
+ Context,
+ &Overload
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
+ Status = DhcpIterateBufferOptions (
+ (UINT8 *) Packet->Dhcp4.Header.BootFileName,
+ 128,
+ Check,
+ Context,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
+ Status = DhcpIterateBufferOptions (
+ (UINT8 *) Packet->Dhcp4.Header.ServerName,
+ 64,
+ Check,
+ Context,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call back function to DhcpIterateOptions to compute each option's
+ length. It just adds the data length of all the occurrences of this
+ Tag. Context is an array of 256 DHCP_OPTION_COUNT.
+
+ @param[in] Tag The current option to check
+ @param[in] Len The length of the option data
+ @param[in] Data The option data
+ @param[in] Context The context, which is a array of 256
+ DHCP_OPTION_COUNT.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+DhcpGetOptionLen (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_OPTION_COUNT *OpCount;
+
+ OpCount = (DHCP_OPTION_COUNT *) Context;
+ OpCount[Tag].Offset = (UINT16) (OpCount[Tag].Offset + Len);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call back function to DhcpIterateOptions to consolidate each option's
+ data. There are maybe several occurrence of the same option.
+
+ @param[in] Tag The option to consolidate its data
+ @param[in] Len The length of option data
+ @param[in] Data The data of the option's current occurrence
+ @param[in] Context The context, which is DHCP_OPTION_CONTEXT. This
+ array is just a wrap to pass THREE parameters.
+
+ @retval EFI_SUCCESS It always returns EFI_SUCCESS
+
+**/
+EFI_STATUS
+DhcpFillOption (
+ IN UINT8 Tag,
+ IN UINT8 Len,
+ IN UINT8 *Data,
+ IN VOID *Context
+ )
+{
+ DHCP_OPTION_CONTEXT *OptContext;
+ DHCP_OPTION_COUNT *OptCount;
+ DHCP_OPTION *Options;
+ UINT8 *Buf;
+ UINT8 Index;
+
+ OptContext = (DHCP_OPTION_CONTEXT *) Context;
+
+ OptCount = OptContext->OpCount;
+ Index = OptCount[Tag].Index;
+ Options = OptContext->Options;
+ Buf = OptContext->Buf;
+
+ if (Options[Index].Data == NULL) {
+ Options[Index].Tag = Tag;
+ Options[Index].Data = Buf + OptCount[Tag].Offset;
+ }
+
+ CopyMem (Buf + OptCount[Tag].Offset, Data, Len);
+
+ OptCount[Tag].Offset = (UINT16) (OptCount[Tag].Offset + Len);
+ Options[Index].Len = (UINT16) (Options[Index].Len + Len);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the options of a DHCP packet. It supports RFC 3396: Encoding
+ Long Options in DHCP. That is, it will combine all the option value
+ of all the occurrences of each option.
+ A little bit of implementation:
+ It adopts the "Key indexed counting" algorithm. First, it allocates
+ an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded
+ as a UINT8. It then iterates the DHCP packet to get data length of
+ each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it
+ knows the number of present options and their length. It allocates a
+ array of DHCP_OPTION and a continuous buffer after the array to put
+ all the options' data. Each option's data is pointed to by the Data
+ field in DHCP_OPTION structure. At last, it call DhcpIterateOptions
+ with DhcpFillOption to fill each option's data to its position in the
+ buffer.
+
+ @param[in] Packet The DHCP packet to parse the options
+ @param[out] Count The number of valid dhcp options present in the
+ packet
+ @param[out] OptionPoint The array that contains the DHCP options. Caller
+ should free it.
+
+ @retval EFI_NOT_FOUND Cannot find any option.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to parse the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formatted
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpParseOption (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT INTN *Count,
+ OUT DHCP_OPTION **OptionPoint
+ )
+{
+ DHCP_OPTION_CONTEXT Context;
+ DHCP_OPTION *Options;
+ DHCP_OPTION_COUNT *OptCount;
+ EFI_STATUS Status;
+ UINT16 TotalLen;
+ INTN OptNum;
+ INTN Index;
+
+ ASSERT ((Count != NULL) && (OptionPoint != NULL));
+
+ //
+ // First compute how many options and how long each option is
+ // with the "Key indexed counting" algorithms.
+ //
+ OptCount = AllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));
+
+ if (OptCount == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Before the loop, Offset is the length of the option. After loop,
+ // OptCount[Index].Offset specifies the offset into the continuous
+ // option value buffer to put the data.
+ //
+ TotalLen = 0;
+ OptNum = 0;
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (OptCount[Index].Offset != 0) {
+ OptCount[Index].Index = (UINT8) OptNum;
+
+ TotalLen = (UINT16) (TotalLen + OptCount[Index].Offset);
+ OptCount[Index].Offset = (UINT16) (TotalLen - OptCount[Index].Offset);
+
+ OptNum++;
+ }
+ }
+
+ *Count = OptNum;
+ *OptionPoint = NULL;
+
+ if (OptNum == 0) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Allocate a buffer to hold the DHCP options, and after that, a
+ // continuous buffer to put all the options' data.
+ //
+ Options = AllocateZeroPool ((UINTN) (OptNum * sizeof (DHCP_OPTION)) + TotalLen);
+
+ if (Options == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Context.OpCount = OptCount;
+ Context.Options = Options;
+ Context.Buf = (UINT8 *) (Options + OptNum);
+
+ Status = DhcpIterateOptions (Packet, DhcpFillOption, &Context);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Options);
+ goto ON_EXIT;
+ }
+
+ *OptionPoint = Options;
+
+ON_EXIT:
+ FreePool (OptCount);
+ return Status;
+}
+
+
+/**
+ Validate the packet's options. If necessary, allocate
+ and fill in the interested parameters.
+
+ @param[in] Packet The packet to validate the options
+ @param[out] Para The variable to save the DHCP parameters.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to validate the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formatted
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpValidateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT DHCP_PARAMETER **Para OPTIONAL
+ )
+{
+ DHCP_PARAMETER Parameter;
+ DHCP_OPTION_FORMAT *Format;
+ DHCP_OPTION *AllOption;
+ DHCP_OPTION *Option;
+ EFI_STATUS Status;
+ BOOLEAN Updated;
+ INTN Count;
+ INTN Index;
+
+ if (Para != NULL) {
+ *Para = NULL;
+ }
+
+ AllOption = NULL;
+
+ Status = DhcpParseOption (Packet, &Count, &AllOption);
+ if (EFI_ERROR (Status) || (Count == 0)) {
+ return Status;
+ }
+ ASSERT (AllOption != NULL);
+
+ Updated = FALSE;
+ ZeroMem (&Parameter, sizeof (Parameter));
+
+ for (Index = 0; Index < Count; Index++) {
+ Option = &AllOption[Index];
+
+ //
+ // Find the format of the option then validate it.
+ //
+ Format = DhcpFindOptionFormat (Option->Tag);
+
+ if (Format == NULL) {
+ continue;
+ }
+
+ if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ //
+ // Get the client interested parameters
+ //
+ if (Format->Alert && (Para != NULL)) {
+ Updated = TRUE;
+ Status = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ if (Updated && (Para != NULL)) {
+ *Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), &Parameter);
+ if (*Para == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ }
+
+ON_EXIT:
+ FreePool (AllOption);
+ return Status;
+}
+
+
+
+/**
+ Append an option to the memory, if the option is longer than
+ 255 bytes, splits it into several options.
+
+ @param[out] Buf The buffer to append the option to
+ @param[in] Tag The option's tag
+ @param[in] DataLen The length of the option's data
+ @param[in] Data The option's data
+
+ @return The position to append the next option
+
+**/
+UINT8 *
+DhcpAppendOption (
+ OUT UINT8 *Buf,
+ IN UINT8 Tag,
+ IN UINT16 DataLen,
+ IN UINT8 *Data
+ )
+{
+ INTN Index;
+ INTN Len;
+
+ ASSERT (DataLen != 0);
+
+ for (Index = 0; Index < (DataLen + 254) / 255; Index++) {
+ Len = MIN (255, DataLen - Index * 255);
+
+ *(Buf++) = Tag;
+ *(Buf++) = (UINT8) Len;
+ CopyMem (Buf, Data + Index * 255, (UINTN) Len);
+
+ Buf += Len;
+ }
+
+ return Buf;
+}
+
+
+/**
+ Build a new DHCP packet from a seed packet. Options may be deleted or
+ appended. The caller should free the NewPacket when finished using it.
+
+ @param[in] SeedPacket The seed packet to start with
+ @param[in] DeleteCount The number of options to delete
+ @param[in] DeleteList The options to delete from the packet
+ @param[in] AppendCount The number of options to append
+ @param[in] AppendList The options to append to the packet
+ @param[out] NewPacket The new packet, allocated and built by this
+ function.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval EFI_INVALID_PARAMETER The options in SeekPacket are mal-formatted
+ @retval EFI_SUCCESS The packet is build.
+
+**/
+EFI_STATUS
+DhcpBuild (
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ )
+{
+ DHCP_OPTION *Mark;
+ DHCP_OPTION *SeedOptions;
+ EFI_DHCP4_PACKET *Packet;
+ EFI_STATUS Status;
+ INTN Count;
+ UINT32 Index;
+ UINT32 Len;
+ UINT8 *Buf;
+
+ //
+ // Use an array of DHCP_OPTION to mark the existence
+ // and position of each valid options.
+ //
+ Mark = AllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);
+
+ if (Mark == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ Mark[Index].Tag = (UINT8) Index;
+ Mark[Index].Len = 0;
+ }
+
+ //
+ // Get list of the options from the seed packet, then put
+ // them to the mark array according to their tags.
+ //
+ SeedOptions = NULL;
+ Status = DhcpParseOption (SeedPacket, &Count, &SeedOptions);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (SeedOptions != NULL) {
+ for (Index = 0; Index < (UINT32) Count; Index++) {
+ Mark[SeedOptions[Index].Tag] = SeedOptions[Index];
+ }
+ }
+
+ //
+ // Mark the option's length is zero if it is in the DeleteList.
+ //
+ for (Index = 0; Index < DeleteCount; Index++) {
+ Mark[DeleteList[Index]].Len = 0;
+ }
+
+ //
+ // Add or replace the option if it is in the append list.
+ //
+ for (Index = 0; Index < AppendCount; Index++) {
+ Mark[AppendList[Index]->OpCode].Len = AppendList[Index]->Length;
+ Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data;
+ }
+
+ //
+ // compute the new packet length. No need to add 1 byte for
+ // EOP option since EFI_DHCP4_PACKET includes one extra byte
+ // for option. It is necessary to split the option if it is
+ // longer than 255 bytes.
+ //
+ Len = sizeof (EFI_DHCP4_PACKET);
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (Mark[Index].Len != 0) {
+ Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len;
+ }
+ }
+
+ Status = EFI_OUT_OF_RESOURCES;
+ Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len);
+
+ if (Packet == NULL) {
+ goto ON_ERROR;
+ }
+
+ Packet->Size = Len;
+ Packet->Length = 0;
+ CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (Packet->Dhcp4.Header));
+ Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
+ Buf = Packet->Dhcp4.Option;
+
+ for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
+ if (Mark[Index].Len != 0) {
+ Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data);
+ }
+ }
+
+ *(Buf++) = DHCP4_TAG_EOP;
+ Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)
+ + (UINT32) (Buf - Packet->Dhcp4.Option);
+
+ *NewPacket = Packet;
+ Status = EFI_SUCCESS;
+
+ON_ERROR:
+ if (SeedOptions != NULL) {
+ FreePool (SeedOptions);
+ }
+
+ FreePool (Mark);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Option.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Option.h
new file mode 100644
index 00000000..fcc984ab
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Dhcp4Dxe/Dhcp4Option.h
@@ -0,0 +1,228 @@
+/** @file
+ To validate, parse and process the DHCP options.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_DHCP4_OPTION_H__
+#define __EFI_DHCP4_OPTION_H__
+
+///
+/// DHCP option tags (types)
+///
+
+#define DHCP_OPTION_MAGIC 0x63538263 // Network byte order
+#define DHCP_MAX_OPTIONS 256
+
+
+//
+// DHCP option types, this is used to validate the DHCP options.
+//
+#define DHCP_OPTION_SWITCH 1
+#define DHCP_OPTION_INT8 2
+#define DHCP_OPTION_INT16 3
+#define DHCP_OPTION_INT32 4
+#define DHCP_OPTION_IP 5
+#define DHCP_OPTION_IPPAIR 6
+
+//
+// Value of DHCP overload option
+//
+#define DHCP_OVERLOAD_FILENAME 1
+#define DHCP_OVERLOAD_SVRNAME 2
+#define 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; // DHCP4_TAG_NETMASK
+ IP4_ADDR Router; // DHCP4_TAG_ROUTER, only the first router is used
+
+ //
+ // DHCP specific options
+ //
+ UINT8 DhcpType; // DHCP4_TAG_MSG_TYPE
+ UINT8 Overload; // DHCP4_TAG_OVERLOAD
+ IP4_ADDR ServerId; // DHCP4_TAG_SERVER_ID
+ UINT32 Lease; // DHCP4_TAG_LEASE
+ UINT32 T1; // DHCP4_TAG_T1
+ UINT32 T2; // DHCP4_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 minimum occurrence of this data type. MaxOccur is defined
+/// similarly. If MaxOccur is -1, it means that there is no limit on the
+/// maximum occurrence. 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
+ );
+
+/**
+ Iterate through a DHCP message to visit each option. First inspect
+ all the options in the OPTION field. Then if overloaded, inspect
+ the options in FILENAME and SERVERNAME fields. One option may be
+ encoded in several places. See RFC 3396 Encoding Long Options in DHCP
+
+ @param[in] Packet The DHCP packet to check the options for
+ @param[in] Check The callback function to be called for each option
+ found
+ @param[in] Context The opaque parameter for Check
+
+ @retval EFI_SUCCESS The DHCP packet's options are well formatted
+ @retval EFI_INVALID_PARAMETER The DHCP packet's options are not well formatted
+
+**/
+EFI_STATUS
+DhcpIterateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ IN DHCP_CHECK_OPTION Check OPTIONAL,
+ IN VOID *Context
+ );
+
+/**
+ Validate the packet's options. If necessary, allocate
+ and fill in the interested parameters.
+
+ @param[in] Packet The packet to validate the options
+ @param[out] Para The variable to save the DHCP parameters.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to validate the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formatted
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpValidateOptions (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT DHCP_PARAMETER **Para OPTIONAL
+ );
+
+/**
+ Parse the options of a DHCP packet. It supports RFC 3396: Encoding
+ Long Options in DHCP. That is, it will combine all the option value
+ of all the occurrences of each option.
+ A little bit of implementation:
+ It adopts the "Key indexed counting" algorithm. First, it allocates
+ an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded
+ as a UINT8. It then iterates the DHCP packet to get data length of
+ each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it
+ knows the number of present options and their length. It allocates a
+ array of DHCP_OPTION and a continuous buffer after the array to put
+ all the options' data. Each option's data is pointed to by the Data
+ field in DHCP_OPTION structure. At last, it call DhcpIterateOptions
+ with DhcpFillOption to fill each option's data to its position in the
+ buffer.
+
+ @param[in] Packet The DHCP packet to parse the options
+ @param[out] Count The number of valid dhcp options present in the
+ packet
+ @param[out] OptionPoint The array that contains the DHCP options. Caller
+ should free it.
+
+ @retval EFI_NOT_FOUND Cannot find any option.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to parse the packet.
+ @retval EFI_INVALID_PARAMETER The options are mal-formatted
+ @retval EFI_SUCCESS The options are parsed into OptionPoint
+
+**/
+EFI_STATUS
+DhcpParseOption (
+ IN EFI_DHCP4_PACKET *Packet,
+ OUT INTN *Count,
+ OUT DHCP_OPTION **OptionPoint
+ );
+
+/**
+ Append an option to the memory, if the option is longer than
+ 255 bytes, splits it into several options.
+
+ @param[out] Buf The buffer to append the option to
+ @param[in] Tag The option's tag
+ @param[in] DataLen The length of the option's data
+ @param[in] Data The option's data
+
+ @return The position to append the next option
+
+**/
+UINT8 *
+DhcpAppendOption (
+ OUT UINT8 *Buf,
+ IN UINT8 Tag,
+ IN UINT16 DataLen,
+ IN UINT8 *Data
+ );
+
+/**
+ Build a new DHCP packet from a seed packet. Options may be deleted or
+ appended. The caller should free the NewPacket when finished using it.
+
+ @param[in] SeedPacket The seed packet to start with
+ @param[in] DeleteCount The number of options to delete
+ @param[in] DeleteList The options to delete from the packet
+ @param[in] AppendCount The number of options to append
+ @param[in] AppendList The options to append to the packet
+ @param[out] NewPacket The new packet, allocated and built by this
+ function.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval EFI_INVALID_PARAMETER The options in SeekPacket are mal-formatted
+ @retval EFI_SUCCESS The packet is build.
+
+**/
+EFI_STATUS
+DhcpBuild (
+ IN EFI_DHCP4_PACKET *SeedPacket,
+ IN UINT32 DeleteCount,
+ IN UINT8 *DeleteList OPTIONAL,
+ IN UINT32 AppendCount,
+ IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket
+ );
+
+#endif