/*==============================================================================
* Dhcp4Dxe - DHCPv4 Protocol Driver Implementation
* Source: e:\hs\AmiNetworkPkg\UefiNetworkStack\Ipv4\Dhcp4Dxe\
* Compilation units: Dhcp4Driver.c, Dhcp4Io.c, Dhcp4Impl.c, Dhcp4Option.c
* Libraries: DxeNetLib, DxeUdpIoLib, DxeDpcLib, UefiLib, BaseLib, BaseMemoryLib
* Binary: PE32+ (x86_64), 130 functions, ~43 KB
*============================================================================*/
#include "Dhcp4Dxe.h"
/*=============================================================================
* External Dependencies / Boot Services Table
*============================================================================*/
extern EFI_HANDLE ImageHandle; // 0x9140
extern EFI_HANDLE ImageHandle_0; // 0x9148
extern UINT64 BootServices; // 0x9DE0 - gBS
extern UINT64 SystemTable; // 0x9DD8 - gST
extern UINT64 RuntimeServices; // 0x9DF0 - gRT
/* Global state for DHCP PXE boot server discovery */
extern UINT64 Qword9DC8; // 0x9DC8 - PXE option cache
extern UINT32 Dword9DD0; // 0x9DD0 - cached client IP for PXE
extern UINT8 Byte9208; // 0x9208 - PXE IP cached flag
extern UINT64 Qword9E18; // 0x9E18 - cached DHCP protocol ptr
extern UINT64 Qword9E00; // 0x9E00 - HOB list pointer
/*=============================================================================
* Forward declarations of local helpers
*============================================================================*/
static UINT64 Dhcp4GetModeData(DHCP_PROTOCOL *This, DHCP4_MODE_DATA *ModeData);
static UINT64 Dhcp4Configure(DHCP_PROTOCOL *This, EFI_DHCP4_CONFIG_DATA *ConfigData);
static UINT64 Dhcp4Initialize(DHCP_PROTOCOL *This, VOID *CompletionEvent);
static UINT64 Dhcp4RenewRebind(DHCP_PROTOCOL *This, BOOLEAN Renew, VOID *CompletionEvent);
static UINT64 Dhcp4Release(DHCP_PROTOCOL *This);
static UINT64 Dhcp4Stop(DHCP_PROTOCOL *This);
static UINT64 Dhcp4Build(EFI_DHCP4_PROTOCOL *This, VOID *Packet, UINT32 *Length, VOID *Options);
static UINT64 Dhcp4TransmitReceive(EFI_DHCP4_PROTOCOL *This, VOID *Token);
static UINT64 Dhcp4Parse(EFI_DHCP4_PROTOCOL *This, VOID *Packet, UINT32 *OptionCount, VOID *OptionList);
/*=============================================================================
* Forward declarations of driver-level functions
*============================================================================*/
static UINT64 Dhcp4DriverSupported(EFI_DRIVER_BINDING_PROTOCOL *This, EFI_HANDLE Controller, EFI_DEVICE_PATH_PROTOCOL *RemainingPath);
static UINT64 Dhcp4DriverStart(EFI_DRIVER_BINDING_PROTOCOL *This, EFI_HANDLE Controller, EFI_DEVICE_PATH_PROTOCOL *RemainingPath);
static UINT64 Dhcp4DriverStop(EFI_DRIVER_BINDING_PROTOCOL *This, EFI_HANDLE Controller, UINTN ChildCount, EFI_HANDLE *ChildHandleBuffer);
static UINT64 Dhcp4ServiceBindingCreateChild(DHCP_PROTOCOL *ServiceBinding, EFI_HANDLE *ChildHandle);
static UINT64 Dhcp4ServiceBindingDestroyChild(EFI_DHCP4_PROTOCOL *Child, EFI_HANDLE *ChildHandle);
static UINT64 Dhcp4CleanupProtocol(DHCP_PROTOCOL *Instance);
static VOID Dhcp4TimerNotify(EFI_EVENT Event, VOID *Context);
/*=============================================================================
* Forward declarations of I/O and state machine functions
*============================================================================*/
static VOID Dhcp4RxCallback(VOID *UdpPacket, VOID *EndPoint, VOID *IoStatus, VOID *Context);
static VOID Dhcp4ReceiveDpc(VOID *UdpPacket, VOID *EndPoint, VOID *IoStatus, VOID *Context);
static UINT64 Dhcp4SendMessage(DHCP_PROTOCOL *Instance, UINT8 *SeedHead, DHCP_LEASE *Para, UINT8 MsgType, VOID *ExtraOptions);
/*==============================================================================
* SECTION 1: Entry Point and Driver Binding Protocol
*============================================================================*/
/*-----------------------------------------------------------------------------
* ModuleEntryPoint (0x528)
* Standard UEFI driver entry point.
* - Calls ProcessLibraryConstructorList (sub_5B0)
* - Installs Driver Binding Protocol on ImageHandle
* - Calls UefiDriverModelRegister (sub_6A4) which reads NetworkStackVar to
* decide whether to install the driver binding protocol.
*----------------------------------------------------------------------------*/
EFI_STATUS
EFIAPI
ModuleEntryPoint(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Interface;
EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
ProcessLibraryConstructorList(ImageHandle, SystemTable);
Status = gBS->OpenProtocol(
ImageHandle,
&gEfiDriverBindingProtocolGuid,
&Interface,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR(Status)) {
ASSERT_EFI_ERROR(Status);
}
DriverBinding = (EFI_DRIVER_BINDING_PROTOCOL *)Interface;
DriverBinding->DriverUnload = Dhcp4DriverUnload;
return UefiDriverModelRegister(ImageHandle, SystemTable);
}
/*-----------------------------------------------------------------------------
* ProcessLibraryConstructorList (sub_5B0, 0x5B0)
* Initializes global pointers: gImageHandle, gST, gBS, gRT.
* Also initializes DPC (Deferred Procedure Call) library.
*----------------------------------------------------------------------------*/
UINT64
ProcessLibraryConstructorList(
UINT64 ImageHandle,
EFI_SYSTEM_TABLE *SystemTable
)
{
gImageHandle = ImageHandle;
ASSERT(gImageHandle != NULL);
gST = SystemTable;
ASSERT(gST != NULL);
gBS = SystemTable->BootServices;
ASSERT(gBS != NULL);
gRT = SystemTable->RuntimeServices;
ASSERT(gRT != NULL);
DpcLibInit();
return gBS->LocateProtocol(&gEfiDpcProtocolGuid, NULL, &gDpcProtocol);
}
/*-----------------------------------------------------------------------------
* UefiDriverModelRegister (sub_6A4, 0x6A4)
* Reads "NetworkStackVar" (GUID: D1405D16-...) from UEFI variables.
* If variable is non-zero (network stack enabled), installs the driver
* binding protocol using gBS->InstallMultipleProtocolInterfaces.
* The protocol instance includes:
* - DriverBinding (off_9120 -> sub_7A4) [Supported]
* - ComponentName (off_9178 -> sub_11CC) [ComponentName]
* - ComponentName2 (off_9160 -> sub_12AC) [ComponentName2]
*----------------------------------------------------------------------------*/
UINT64
UefiDriverModelRegister(
UINT64 ImageHandle,
EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINTN DataSize;
UINT8 NetworkStackEnabled;
CHAR16 VariableName[] = L"NetworkStackVar";
DataSize = sizeof(NetworkStackEnabled);
Status = gRT->GetVariable(
VariableName,
&gAmiNetworkStackVarGuid,
NULL,
&DataSize,
&NetworkStackEnabled
);
if (!EFI_ERROR(Status)) {
if (NetworkStackEnabled) {
return gBS->InstallMultipleProtocolInterfaces(
&gImageHandle,
&gEfiDriverBindingProtocolGuid,
&gDriverBindingProtocol,
&gEfiComponentName2ProtocolGuid,
&gComponentName2,
&gEfiComponentNameProtocolGuid,
&gComponentName,
NULL
);
}
return EFI_NOT_STARTED;
}
return Status;
}
/*-----------------------------------------------------------------------------
* Dhcp4DriverUnload (sub_3A4, 0x3A4)
* Called when the driver is unloaded. Enumerates all handles in the system
* that have the DHCP4 Service Binding protocol installed, and for each one:
* 1. Closes all child protocol instances (EFI_DHCP4_PROTOCOL)
* 2. Uninstalls the DHCP4 Service Binding protocol from the controller
* 3. Uninstalls MNP and IP4 child instances
* The loop uses gBS->LocateHandleBuffer to find all handles.
*----------------------------------------------------------------------------*/
UINT64
Dhcp4DriverUnload(
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiDhcp4ServiceBindingProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (!EFI_ERROR(Status)) {
for (Index = 0; Index < HandleCount; Index++) {
UINT64 Dhcp4Sb;
UINTN ChildIndex;
if (!EFI_ERROR(gBS->OpenProtocol(
HandleBuffer[Index],
&gEfiDhcp4ServiceBindingProtocolGuid,
&Dhcp4Sb,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
))) {
// Destroy all child instances
for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {
gBS->CloseProtocol(
HandleBuffer[ChildIndex],
*(UINT64 *)(Dhcp4Sb + 40), // Dhcp4Sb->ImageHandle
0
);
}
// Uninstall from controller
gBS->UninstallProtocolInterface(
*(UINT64 *)(Dhcp4Sb + 40), // Dhcp4Sb->Controller
&gEfiDhcp4ServiceBindingProtocolGuid,
Dhcp4Sb
);
// Also uninstall MNP and IP4 child
if (!EFI_ERROR(gBS->OpenProtocol(
HandleBuffer[Index],
&gEfiIp4ProtocolGuid,
&ChildIndex,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
))) {
gBS->UninstallProtocolInterface(
*(UINT64 *)(Dhcp4Sb + 40),
&gEfiIp4ProtocolGuid,
ChildIndex
);
}
if (!EFI_ERROR(gBS->OpenProtocol(
HandleBuffer[Index],
&gEfiManagedNetworkProtocolGuid,
&ChildIndex,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
))) {
gBS->UninstallProtocolInterface(
*(UINT64 *)(Dhcp4Sb + 40),
&gEfiManagedNetworkProtocolGuid,
ChildIndex
);
}
}
}
if (HandleBuffer) {
gBS->FreePool(HandleBuffer);
}
}
return EFI_SUCCESS;
}
/*-----------------------------------------------------------------------------
* Global driver binding protocol instance (at 0x9120)
* Components:
* - off_9120 -> sub_7A4 = Dhcp4DriverSupported
* - off_9128 -> sub_A28 = Dhcp4DriverStart
* - off_9130 -> sub_BA8 = Dhcp4DriverStop
* - off_9138 -> 0x0A = Version (1.0)
* - off_9140 -> gImageHandle
* - off_9148 -> gImageHandle_0
*----------------------------------------------------------------------------*/
EFI_DRIVER_BINDING_PROTOCOL gDriverBindingProtocol = {
Dhcp4DriverSupported,
Dhcp4DriverStart,
Dhcp4DriverStop,
0x0A, // Version 1.0
NULL, // ImageHandle (filled at runtime)
NULL // DriverBindingHandle
};
/*-----------------------------------------------------------------------------
* Dhcp4DriverSupported (sub_7DC, 0x7DC)
* Checks if the controller supports DHCP4 by verifying:
* 1. The MNP Service Binding protocol is present
* 2. Opening UDP4 protocol BY_DRIVER succeeds
* Uses a test configuration:
* - UdpVersion = 4 (0x1000001)
* - StationPort = 68
* - RemotePort = 67
* - Timeout = 0x4000 (16384 microseconds)
*----------------------------------------------------------------------------*/
UINT64
Dhcp4DriverSupported(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_UDP4_CONFIG_DATA UdpConfigData;
UINT64 Status;
UdpConfigData.AcceptBroadcast = TRUE;
UdpConfigData.AcceptPromiscuous = FALSE;
UdpConfigData.AcceptAnyPort = FALSE;
UdpConfigData.AcceptMulticast = FALSE;
UdpConfigData.UseDefaultAddress = FALSE;
UdpConfigData.StationPort = 68; // DHCP client port
UdpConfigData.RemotePort = 67; // DHCP server port
UdpConfigData.TypeOfService = 0;
UdpConfigData.TimeToLive = 0;
UdpConfigData.Timeout = 16384; // ~16ms
// Generate 4 random bytes for client hardware address seed
NetGetRandomBytes((UINT8 *)UdpConfigData.StationAddress, 4);
NetGetRandomBytes((UINT8 *)UdpConfigData.SubnetMask, 4);
NetGetRandomBytes((UINT8 *)UdpConfigData.StationAddress + 4, 4);
return Udp4->Configure(Udp4, &UdpConfigData);
}
/*-----------------------------------------------------------------------------
* Dhcp4DriverStart (sub_A28, 0xA28)
* Called when the driver should start managing a controller.
* 1. Opens the MNP Service Binding protocol on the controller (BY_DRIVER|EXCLUSIVE)
* 2. Creates a DHCP4 child via ServiceBindingCreateChild
* 3. Sets up UDP receive using UdpIoRecvDatagram
* 4. Creates a timer event for retry/timeout handling
* 5. Installs the EFI_DHCP4_PROTOCOL on the child handle
*----------------------------------------------------------------------------*/
UINT64
Dhcp4DriverStart(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_HANDLE ChildHandle;
DHCP_PROTOCOL *Instance;
UINT64 UdpIo;
// Open MNP service binding
Status = gBS->OpenProtocol(
Controller,
&gEfiManagedNetworkServiceBindingProtocolGuid,
NULL,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_ALREADY_STARTED;
}
// Create child instance
Status = Dhcp4ServiceBindingCreateChild(Controller, &ChildHandle);
if (EFI_ERROR(Status)) {
return Status;
}
Instance = CR(ChildHandle, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
ASSERT(Instance != NULL);
// Set up UDP receive on the child's UdpIo
UdpIo = Instance->UdpIo;
Status = UdpIoRecvDatagram(UdpIo, Dhcp4RxCallback, Instance, 0);
if (EFI_ERROR(Status)) {
Dhcp4CleanupProtocol(Instance);
FreePool(Instance);
return Status;
}
// Create timer event for retry/timeout
Status = gBS->CreateEvent(
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
Dhcp4TimerNotify,
Instance,
&Instance->TimerEvent
);
if (EFI_ERROR(Status)) {
Dhcp4CleanupProtocol(Instance);
return Status;
}
// Install DHCP4 protocol on the child handle
Status = gBS->InstallProtocolInterface(
&ChildHandle,
&gEfiDhcp4ProtocolGuid,
EFI_NATIVE_INTERFACE,
Instance + 8 // EFI_DHCP4_PROTOCOL follows DHCP_PROTOCOL header
);
if (EFI_ERROR(Status)) {
Dhcp4CleanupProtocol(Instance);
return Status;
}
return EFI_SUCCESS;
}
/*-----------------------------------------------------------------------------
* Dhcp4DriverStop (sub_BA8, 0xBA8)
* 1. Locates the DHCP4 child on the controller via MNP children list
* 2. Opens EFI_DHCP4_PROTOCOL with GET_PROTOCOL + BY_CHILD
* 3. Validates the DHCP_PROTOCOL signature (0x50434844 = 'DHCP')
* 4. Checks if the child's request list is empty
* 5. Uninstalls the protocol, cleans up UdpIo, and frees the instance
*----------------------------------------------------------------------------*/
UINT64
Dhcp4DriverStop(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
DHCP_PROTOCOL *ChildInstance;
VOID *Interface;
// Get list of MNP children
Status = gBS->OpenProtocolInformation(
Controller,
&gEfiManagedNetworkProtocolGuid,
&HandleBuffer,
&HandleCount
);
if (EFI_ERROR(Status)) {
return EFI_DEVICE_ERROR;
}
// Find child with DHCP4 protocol
ChildInstance = NULL;
for (Index = 0; Index < HandleCount; Index++) {
if ((HandleBuffer[Index] & 0x10) != 0) { // BY_CHILD_CONTROLLER
ChildInstance = (DHCP_PROTOCOL *)HandleBuffer[Index + 1];
break;
}
}
gBS->FreePool(HandleBuffer);
if (ChildInstance == NULL) {
return EFI_DEVICE_ERROR;
}
// Validate child via open protocol
Status = gBS->OpenProtocol(
ChildInstance,
&gEfiDhcp4ProtocolGuid,
&Interface,
This->DriverBindingHandle,
ChildInstance,
EFI_OPEN_PROTOCOL_GET_PROTOCOL | EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR(Status)) {
return EFI_DEVICE_ERROR;
}
// Validate DHCP_PROTOCOL signature
Dhcp4Instance = CR(Interface, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Dhcp4Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUG_ERROR, "CR has Bad Signature\n"));
}
// Check if requests list is empty
if (!IsListEmpty(&Dhcp4Instance->ParaList)) {
return EFI_ACCESS_DENIED;
}
// If no active instance was found, check if single child stop
if (NumberOfChildren == 0) {
if (!IsListEmpty(&Dhcp4Instance->ParaList)) {
return EFI_ACCESS_DENIED;
}
}
// Clean up and uninstall
Dhcp4Instance->IoStatus = 2; // Mark as stopped
gBS->UninstallProtocolInterface(
ChildInstance,
&gEfiDhcp4ProtocolGuid,
Interface
);
Dhcp4CleanupProtocol(Dhcp4Instance);
// Close DPC if pending
if (Qword9DC8 != 0) {
DpcLibDestroy();
Qword9DC8 = 0;
}
FreePool(Dhcp4Instance);
return EFI_SUCCESS;
}
/*==============================================================================
* SECTION 2: Service Binding Protocol
*============================================================================*/
/*-----------------------------------------------------------------------------
* Dhcp4ServiceBindingCreateChild (sub_8C8, 0x8C8)
* Creates a new DHCP child instance (DHCP_PROTOCOL).
* 1. Allocates and zeros 360-byte DHCP_PROTOCOL structure
* 2. Sets signature = 0x50434844 ('DHCP')
* 3. Stores Controller and Image handles
* 4. Initializes the child request list (ParaList)
* 5. Generates random XID using NetRandomInitSeed
* 6. Copies DHCP configuration block from config table (off_9150 -> sub_E30)
* 7. Creates timer event for timeout/retry (sub_2894 timer callback)
* 8. Creates UdpIo via UdpIoCreatePort (sub_72F8), with Dhcp4DriverSupported (sub_7DC)
* as the configuration callback
* 9. Copies MAC address and HW addr type/length from SNP
*----------------------------------------------------------------------------*/
UINT64
Dhcp4ServiceBindingCreateChild(
IN UINT64 Controller,
IN UINT64 Image,
OUT EFI_HANDLE *ChildHandle
)
{
DHCP_PROTOCOL *Instance;
EFI_STATUS Status;
UINT64 ConfigTable;
UINT64 UdpIo;
*ChildHandle = NULL;
Instance = AllocateZeroPool(sizeof(DHCP_PROTOCOL));
if (Instance == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Instance->Signature = DHCP_PROTOCOL_SIGNATURE; // 0x50434844
Instance->Controller = Controller;
Instance->Image = Image;
// Initialize child list head
InitializeListHead(&Instance->ParaList);
// Null out offer/lease pointers (initially empty)
Instance->ParsedList = NULL;
Instance->SelectedOffer = NULL;
// Generate random XID
Instance->Xid = (NET_RANDOM_SEED * NetRandomInitSeed() + 12345) % 0xFFFFFFFF;
// Copy DHCP configuration block (function pointer table)
ConfigTable = (UINT64)&gEfiDhcp4ProtocolGuid;
CopyMem(&Instance->Link, ConfigTable, sizeof(EFI_DHCP4_PROTOCOL));
// Create timer event for DHCP retry/timeout processing
Status = gBS->CreateEvent(
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
Dhcp4TimerNotify,
Instance,
&Instance->TimerEvent
);
if (EFI_ERROR(Status)) {
Dhcp4CleanupProtocol(Instance);
FreePool(Instance);
return Status;
}
// Set timer to fire periodically (50ms)
gBS->SetTimer(Instance->TimerEvent, TimerPeriodic, 50000);
// Create UDP I/O for DHCP communication (port 68)
UdpIo = UdpIoCreatePort(
Controller,
Image,
Dhcp4DriverSupported, // Config callback
0,
0
);
Instance->UdpIo = UdpIo;
if (Instance->UdpIo == NULL) {
Dhcp4CleanupProtocol(Instance);
FreePool(Instance);
return EFI_OUT_OF_RESOURCES;
}
// Copy hardware address from SNP
Instance->HwAddrType = *(UINT8 *)(Instance->UdpIo + 68);
Instance->HwAddrLen = *(UINT8 *)(Instance->UdpIo + 712);
CopyMem(Instance->MacAddr, Instance->UdpIo + 616, 32);
*ChildHandle = Instance;
return EFI_SUCCESS;
}
/*-----------------------------------------------------------------------------
* Dhcp4ServiceBindingDestroyChild (sub_FA8, 0xFA8)
* Destroys a DHCP child instance opened by CreateChild.
* 1. Validates the child via EFI_SERVICE_BINDING_PROTOCOL
* 2. Validates DHCP_PROTOCOL signature via CR macro
* 3. Confirms child belongs to this service binding instance
* 4. If already marked for destruction, returns success
* 5. Closes all protocol handles opened for this child
* 6. Removes from protocol database
* 7. Closes UDP IO, cleans up selected offer/parse list
* 8. Frees the instance
* 9. Notifies the MNP layer of child destruction
*----------------------------------------------------------------------------*/
UINT64
Dhcp4ServiceBindingDestroyChild(
IN EFI_DHCP4_PROTOCOL *This,
IN EFI_HANDLE *ChildHandle
)
{
EFI_STATUS Status;
DHCP_PROTOCOL *Instance;
UINT64 Interface;
UINT64 Temp;
if (This == NULL || ChildHandle == NULL) {
return EFI_INVALID_PARAMETER;
}
// Get the protocol instance from the child handle
Status = gBS->OpenProtocol(
ChildHandle,
&gEfiServiceBindingProtocolGuid,
&Interface,
gImageHandle_0,
ChildHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL | EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
// Validate DHCP4 protocol signature
Instance = CR(Interface, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUG_ERROR, "CR has Bad Signature\n"));
}
// Verify this child belongs to this service binding
if ((UINT64 *)Instance->Image != This) {
return EFI_INVALID_PARAMETER;
}
// Already destroyed?
if (Instance->HwAddrType != 0) {
return EFI_SUCCESS;
}
// Mark as destroying
Instance->HwAddrType = 1;
// Close protocol handles on the controller
Temp = gBS->FreePool(0);
gBS->CloseProtocol(
*(UINT64 *)(Instance->UdpIo + 56) + 56,
&gEfiManagedNetworkProtocolGuid,
gImageHandle_0,
ChildHandle
);
if (EFI_ERROR(Status)) {
Instance->HwAddrType = 0;
return Status;
}
// Clear the DHCP configuration if this Instance was configured
if (Instance->ParaList.Flink == Instance) {
Dhcp4Configure(Instance, NULL);
}
// Remove from child list and decrement count
RemoveEntryList(&Instance->Link);
Instance->ChildCount--;
// Clean up UDP IO
if (Instance->UdpIoProx) {
UdpIoDestroyPort(&Instance->UdpIoProx);
UdpIoCleanUdp4Protocol(
*(UINT64 *)(Instance->UdpIoProx + 56),
&gEfiManagedNetworkProtocolGuid,
*(UINT64 *)(Instance->UdpIoProx->Image),
Instance->ChildHandle
);
UdpIoFreePort(Instance->UdpIoProx);
Instance->UdpIoProx = NULL;
Instance->ChildHandle = NULL;
}
// Close protocol on service binding
gBS->FreePool(Temp);
FreePool(Instance);
return EFI_SUCCESS;
}
/*-----------------------------------------------------------------------------
* Dhcp4CleanupProtocol (sub_864, 0x864)
* Resets a DHCP protocol Instance back to initial state (Stopped).
* - Clears UDP IO references
* - Frees any parse list and selected offer
* - Resets state to Stopped
*----------------------------------------------------------------------------*/
UINT64
Dhcp4CleanupProtocol(
IN DHCP_PROTOCOL *Instance
)
{
// Reset retry state
Instance->Xid++;
Instance->ClientAddr = 0;
Instance->Netmask = 0;
Instance->State = 1; // Init
// Free parsed offer list
if (Instance->ParsedList != NULL) {
NetbufFree(Instance->ParsedList);
Instance->ParsedList = NULL;
}
// Free selected offer
if (Instance->SelectedOffer != NULL) {
UdpIoFreePort(Instance->SelectedOffer);
Instance->SelectedOffer = NULL;
}
// Free last sent packet
if (Instance->LastPacket != NULL) {
NetbufFree(Instance->LastPacket);
Instance->LastPacket = NULL;
}
// Clear results
Instance->Result = 0;
Instance->RetryCount = 0;
Instance->MaxRetryCount = 0;
// Close UDP IO (UdpIoProx)
if (Instance->UdpIoProx != NULL) {
UdpIoFreePort(Instance->UdpIoProx);
Instance->UdpIoProx = NULL;
}
// Reset retry counters
Instance->TriesCount = 0;
Instance->MaxTries = 0;
Instance->ProbeCount = 0;
// Clean parameter list
NetListClean(&Instance->ParaList);
return EFI_SUCCESS;
}
/*==============================================================================
* SECTION 3: DHCP Protocol API
*============================================================================*/
/*-----------------------------------------------------------------------------
* EFI_DHCP4_PROTOCOL dispatch table (at 0x9150-0x91A8)
* off_9150 -> sub_E30 = Dhcp4ProtocolInstall
* off_9158 -> sub_FA8 = Dhcp4ProtocolUninstall
* off_9160 -> sub_11CC = Dhcp4ComponentNameGetName
* off_9168 -> sub_12AC = Dhcp4ComponentNameGetLanguage
* off_9170 -> 0x79C4 = "en" (supported language)
* off_9178 -> sub_11CC = Dhcp4ComponentNameGetName (also ComponentName)
* off_9180 -> sub_12AC = Dhcp4ComponentNameGetLanguage (also ComponentName2)
* off_9190 -> 0x79C8 = "eng;en" (supported languages)
* off_91C0 -> sub_2ACC = Dhcp4GetModeData
* off_91D0 -> sub_2EE4 = Dhcp4Configure
* off_91E0 -> sub_30E0 = Dhcp4Initialize
* off_91F0 -> sub_32D0 = Dhcp4RenewRebind
* off_9200 -> sub_34B0 = Dhcp4Release
* off_9210 -> sub_3598 = Dhcp4Stop
* plus Build, TransmitReceive, Parse
*----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
* Dhcp4GetModeData (sub_2ACC, 0x2ACC)
* EFI_DHCP4_PROTOCOL.GetModeData()
* Returns the current DHCP mode data (state, IP addresses, lease info).
* If ModeData is NULL, just returns EFI_INVALID_PARAMETER.
* Fills in:
* - State
* - ConfigData (72 bytes copied from Instance+240)
* - ClientMacAddr (32 bytes copied from Instance+176)
* - ServerId, ClientAddr, SubnetMask, Gateway (in network order)
* - Lease info (LeaseTime, LeaseStart)
*----------------------------------------------------------------------------*/
UINT64
Dhcp4GetModeData(
IN EFI_DHCP4_PROTOCOL *This,
OUT DHCP4_MODE_DATA *ModeData
)
{
DHCP_PROTOCOL *Instance;
if (This == NULL || ModeData == NULL) {
return EFI_INVALID_PARAMETER;
}
// CR macro validation
Instance = CR(This, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUG_ERROR, "CR has Bad Signature\n"));
}
// Get the actual Instance (ServiceBinding data)
Instance = *(DHCP_PROTOCOL **)((UINT64)Instance + 104);
ModeData->State = *(UINT32 *)(Instance->ParsedList + 72);
CopyMem(ModeData->ConfigData, Instance->ParsedList + 240, 72);
CopyMem(ModeData->ClientMacAddr, Instance->ParsedList + 176, 32);
// Convert addresses from network to host byte order
ModeData->ServerId = NetGetUint32(Instance->ParsedList + 92);
ModeData->SubnetMask = NetGetUint32(Instance->ParsedList + 96);
ModeData->Gateway = NetGetUint32(Instance->ParsedList + 100);
// Lease info
if (Instance->Lease != NULL) {
ModeData->LeaseTime = NetGetUint32(Instance->Lease + 4);
ModeData->LeaseStartTime = *(UINT32 *)(Instance->Lease + 16);
} else {
ZeroMem(ModeData + 120, 4);
*(UINT32 *)(ModeData + 128) = (UINT32)-1;
}
ModeData->LeaseStart = Instance->ParsedList + 112;
return EFI_SUCCESS;
}
/*-----------------------------------------------------------------------------
* Dhcp4Configure (sub_2EE4, 0x2EE4)
* EFI_DHCP4_PROTOCOL.Configure()
* Configures the DHCP instance with new configuration data.
* - Validates parameters (no NULL data with non-zero lengths)
* - If ConfigData is NULL, resets to default (disabled)
* - Clears parameter list, sets state to Stopped
* - If ConfigData is provided:
* - Validates state: must be Stopped, Init, or Selecting
* - Copies configuration into Instance
* - Applies client address if provided
* - Sets state to Init (or InitReboot if client address given)
* - Initializes retry counts from configuration
*----------------------------------------------------------------------------*/
UINT64
Dhcp4Configure(
IN EFI_DHCP4_PROTOCOL *This,
IN EFI_DHCP4_CONFIG_DATA *ConfigData
)
{
DHCP_PROTOCOL *Instance;
UINT64 TempBuffer;
DHCP_PROTOCOL *ServiceBinding;
UINT64 TryCount;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
if (ConfigData != NULL) {
// Validate parameter list - if count given, buffer must be non-NULL
if (ConfigData->OptionCount != 0 && ConfigData->OptionList == NULL) {
return EFI_INVALID_PARAMETER;
}
if (ConfigData->OverrideCount != 0 && ConfigData->OverrideList == NULL) {
return EFI_INVALID_PARAMETER;
}
}
// CR validation
Instance = CR(This, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUG_ERROR, "CR has Bad Signature\n"));
}
// Verify instance is valid
if (*(UINT32 *)Instance != DHCP_PROTOCOL_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
TempBuffer = gBS->AllocatePool(8);
ServiceBinding = *(DHCP_PROTOCOL **)((UINT64)Instance + 104);
// Check current state - only certain states allow reconfiguration
if (ServiceBinding->State <= 7) {
UINT64 StateMask = (1ULL << 0) | (1ULL << 1) | (1ULL << 7);
if (StateMask & (1ULL << ServiceBinding->State)) {
// Valid state transition
// Ensure this Instance is the one currently configured
if (ServiceBinding->ParaList.Flink != Instance &&
ServiceBinding->ParaList.Flink != NULL)
{
return EFI_ACCESS_DENIED;
}
if (ConfigData != NULL) {
// Save new config
NetListClean(&ServiceBinding->ParaList);
if (Dhcp4SaveConfigData(&ServiceBinding->ParaList, ConfigData) >= 0) {
// Apply the configuration
ServiceBinding->OptionCount = 0;
// Set option total size
if (ConfigData->OptionCount != 0) {
for (TryCount = 0; TryCount < ConfigData->OptionCount; TryCount++) {
ServiceBinding->OptionCount +=
*(UINT8 *)(*(UINT64 *)(ConfigData->OptionList) + 8 * TryCount + 1) + 2;
}
}
// Associate this Instance with the service binding
ServiceBinding->ParaList.Flink = (LIST_ENTRY *)Instance;
// If current state is Stopped (0), transition to Init (1) or InitReboot (7)
if (ServiceBinding->State == 0) {
// Copy client address
ServiceBinding->ClientAddr = NetGetUint32(ConfigData + 32);
ServiceBinding->State = (ServiceBinding->ClientAddr != 0) ? 7 : 1;
}
ServiceBinding->IoStatus = 1; // Configured
Status = EFI_SUCCESS;
}
} else if (ServiceBinding->ParaList.Flink == Instance) {
// Reset this Instance's configuration
Status = EFI_SUCCESS;
Dhcp4ResetConfig(ServiceBinding);
}
}
}
gBS->FreePool(TempBuffer);
return Status;
}
/*-----------------------------------------------------------------------------
* Dhcp4Initialize (sub_30E0, 0x30E0)
* EFI_DHCP4_PROTOCOL.Initialize()
* Starts the DHCP state machine.
* 1. Checks media present via UdpIoCheckMediaStatus
* 2. Validates current state (must be Init=1 or InitReboot=7)
* 3. Transitions to Selecting (2) or Rebooting (8)
* 4. Sends DHCPDISCOVER or DHCPREQUEST (InitReboot)
* 5. If CompletionEvent is NULL, polls in a loop until complete
*----------------------------------------------------------------------------*/
UINT64
Dhcp4Initialize(
IN EFI_DHCP4_PROTOCOL *This,
IN VOID *CompletionEvent // NULL = synchronous
)
{
DHCP_PROTOCOL *Instance;
UINT64 Temp;
DHCP_PROTOCOL *ServiceBinding;
BOOLEAN MediaPresent;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
// CR macro validation
Instance = CR(This, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUG_ERROR, "CR has Bad Signature\n"));
}
if (*(UINT32 *)Instance != DHCP_PROTOCOL_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
Temp = gBS->AllocatePool(8);
ServiceBinding = *(DHCP_PROTOCOL **)((UINT64)Instance + 104);
// Check media present
UdpIoCheckMediaStatus(ServiceBinding->UdpIo[4], &MediaPresent);
if (!MediaPresent) {
DEBUG((DEBUG_ERROR, "In EfiDhcp4Start MediaPresent Status = %r\n", EFI_NOT_READY));
gBS->FreePool(Temp);
return EFI_NOT_READY;
}
// Validate current state
if (ServiceBinding->State == 0) {
gBS->FreePool(Temp);
return EFI_NOT_STARTED;
}
if (ServiceBinding->State != 1 && ServiceBinding->State != 7) {
gBS->FreePool(Temp);
return EFI_ALREADY_STARTED;
}
// Transition to active state
ServiceBinding->Result = EFI_ALREADY_STARTED;
Status = Dhcp4StateMachineStart(ServiceBinding);
if (EFI_ERROR(Status)) {
gBS->FreePool(Temp);
return Status;
}
// Store completion event
Instance->Link.Flink = (LIST_ENTRY *)CompletionEvent;
if (CompletionEvent != NULL) {
return EFI_SUCCESS; // Asynchronous
}
// Synchronous: poll until done
if (Temp != 4) {
gBS->FreePool(4);
}
while (ServiceBinding->Result == EFI_ALREADY_STARTED) {
// Check for timeout via ST->RuntimeServices timer
if (gRT != NULL &&
gRT->GetTime(&gST->RuntimeServices->GetTime)) {
return EFI_TIMEOUT;
}
// Poll the UDP IO for received packets
UdpIoPoll(*(UINT64 *)(ServiceBinding->UdpIo[19] + 744));
}
return ServiceBinding->Result;
}
/*-----------------------------------------------------------------------------
* Dhcp4RenewRebind (sub_32D0, 0x32D0)
* EFI_DHCP4_PROTOCOL.RenewRebind()
* Initiates lease renewal or rebinding.
* - State must be Bound (4)
* - Sets state to Renewing (5) or Rebinding (6)
* - Sends DHCPREQUEST to server or broadcast
* - If CompletionEvent is NULL, polls synchronously
*----------------------------------------------------------------------------*/
UINT64
Dhcp4RenewRebind(
IN EFI_DHCP4_PROTOCOL *This,
IN BOOLEAN Renew,
IN VOID *CompletionEvent
)
{
DHCP_PROTOCOL *Instance;
UINT64 Temp;
DHCP_PROTOCOL *ServiceBinding;
UINT64 RetryCnt;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = CR(This, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUG_ERROR, "CR has Bad Signature\n"));
}
if (*(UINT32 *)Instance != DHCP_PROTOCOL_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
Temp = gBS->AllocatePool(8);
ServiceBinding = *(DHCP_PROTOCOL **)((UINT64)Instance + 104);
if (ServiceBinding->State == 0) {
gBS->FreePool(Temp);
return EFI_NOT_STARTED;
}
if (ServiceBinding->State != 4) {
gBS->FreePool(Temp);
return EFI_NOT_READY;
}
// Check if we have a valid lease
if (ServiceBinding->Lease == NULL ||
ServiceB inding->Lease[8] == 0) { e
gBS->FreePool(Temp);
return EFI_SUCCESS;
}
// Start renew/rebind process
RetryCn = Servi eBinding->RetryCryRangege[0]; // default max tries
ServiceBinding->TriesCount = 0;
ServiceBinding->MaxTries = 0;
ServiceBinding->MaxTries = 0;
if (Renew) {
ServiceBinding->State = 5; // Reneewing
} else {
ServiceBinding->State = 6; // Rebininding
}
if (RetryCnt == 0) {
RetryCnt = 4;
}
ServiceBinding->MaxTries = RetryCnt;
// Mark as renewee/rebind phase
Instance->ink->Flink = (LIST_ENTRY *)CompletionEvent;
// Send DHCP_REQUEST (type 3)
Status = Dhcp4SendMessage(ServiceBinding,
ServiceBinding->SelectedOffer,
ServiceBinding->Lease,
3, // DHCP_MSG_REQUEUEST
"Extra renew/rebind by the application"
);
if (EFI_ERROR(Status)) {
// Failaill back to bound state
ServiceBinding->TriesCount = 0;
ServiceBinding->MaxTries = 0;
ServiceBinding->RetryTimer = 0;
ServiceBinding->State = 4;
gBS->FreePool(Temp);
return Status;
}
ServiceBinding->RenewRebind = 1;
ServiceBinding->Result = EFI_ALREADY_STARTED;
// Store completion event
Instance->MaxTries = (UINT64)CompletionEvent;
if (CompletionEvent != NULL)) {
return EFI_SUCCESS; // Async
}
// Sync: poll until donene
while (ServiceBinding->Result == EFI_ALREADY_STARTED) {
UdpIoPoll(*(INT64 *)(ServiceBinding->UdpIo[19] + 744));
}
return ServiceBinding->Result;
}
/*-----------------------------------------------------------------------------
* Dhcp4Releasee (sub_34B0, 0x34B0)
* EFI_DHCP4_PROTOCOL.Re.easease()
* Releaaseses the currenent DHCP lease, returning to Init state.
* Only valid in Bound (4), Renewing (5), or Rebinding (6) states.
*-----------------------------------------------------------------------------*/
UINT64
Dhcp4Release(
IN EFI_DHCP4_PROTOCOL *Thiis
)
{
DHCP_PROTOCOL *Instance;
UINT64 Temp;
DHCP_PROTOCOL *ServiceBinding;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = CR(This, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUG_ERROR, "CR has Bad Signnature\n"));
}
if (*(UINT32 *)Instance != DHCCH_POTOCOL_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
Temp = gBS->AllocatePool(8);
ServiceBinding = *(DHCP_PROTOCOL **)((UINT64)Instance + 104);
// Check current state - only release if in bound/renew/rebind
if (ServiceBinding->State == 7 || ServiceBinding->State == 4) {
UDP_IO *UdpIo;
UdpIo = ServiceBinding->UdpIo[15];
if (UdpIo != NULL && *(UINT8 *)(UdpIo + 8) != 0 &&
ServiceBinding->State == 4) {
// Send DHCPDECLINE (type 7) or just reset
Status = Dhcp4SendMessage(ServiceBinding,
ServiceBinding->SelectedOffer,
UdpIo,
7, // DHCP_MSG_RELEASE
0);
if (EFI_ERROR(Status)) {
Status = EFI_DEVICE_ERROR;
}
}
// Reset to Init state
Dhcp4ResetProtocol(ServiceBinding);
} else {
Status = EFI_NOT_READY;
}
gBS->FreePool(Temp);
return Status;
}
/*-----------------------------------------------------------------------------
* Dhcp4Stop (sub_3598, 0x3598)
* EFI_DHCP4_PROTOCOL.Stopop()
* Stops the DHCP state machine and resets to Stopped (0) state.
*----------------------------------------------------------------------------*/
UINT64
Dhcp4Stop(
IN EFI_DHCP4_PROTOCOL *This
)
{
DHCP_PROTOCOL *Instance;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = CR(This, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUB_ERROR, "CR has Bad Signature\n"));
}
if (*(UINT32 *)Instance != DHCP_PROTOCOL_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
Temp = gBS->AllocatePool(8);
ServiceBinding = *(DHCP_PROTOCOL **)((UIINT64)Instance + 104);
// Reset the Instance
Dhcp4ResetProtocol(ServiceBinding);
ServiceBinding->State = 0; // Stopped
ServiceBinding->IoStatus = 0; // Not configured
gBS->FreePool(Temp);
return EFI_SUCCESS;
}
/*-----------------------------------------------------------------------------
* Dhcp4Build (sub_3624, 0x3624)
* EFI_DHCP4_PROTOCOL.Build()
* Builds a DHCP packet from the given paramters.
*----------------------------------------------------------------------------*/
UINT64
Dhcp4Build(
IN EFI_DHCP4_PROTOCOL *This,
IN VOID *Packet,
IN UINT32 *Length,
IN VOID *Options
)
{
if (Packet != NULL &&
Options != NULL &&
*(UINT32 *)(Packet + 244) == 1666417251 && // Magiic: 0x63E3E3
Dhcp4ParseOptions(Packet, NULL) >= 0)
{
// Valid packet header
if (Length != NULL) {
if (Options != NULL) {
// Build with both options and data
} }else {
// Build with one options only
}
}
return Dhcp4BuildPacket(Packet, Length, Options, OptionCount, OptionsBuf, Options);
}
return EFI_INVALID_PARAMETER;
}
/*----------------------------------------------------------------------------- -
* Dhcp4TransmitReceiveive (sub_3C08, 0x3C08)
* EFI_DHCP4_PROTOCOL.TransmitReceive()
* Transmits a DHCP packet and waits for a response.
*----------------------------------------------------------------------------*/
UINT64
Dhcp4TransmitReceive(
IN EFI_DHCP4_PROTOCOL *This,
IN VOID *Token
)
{
DHCP_PROTOCOL *Instance;
DHCP_PROTOCOL *ServiceBinding;
VOID *ResponsePacket;
UIINT64 Status;
UINT64 Temp;
if (This == NULL || Token == NULL ||
*(UINT64 *)(Token + 48) == NULL) {
return EFI_INVALID_PARAMETER;
}
// CR validation
Instance = CR(This, DHCP_PROTOCOL, Signature, DHCP_PROTOCOL_SIGNATURE);
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
DEBUG((DEBUB_ERROR, "CR has Bad Bad Signature\n"));
}
ServiceBinding = *(DHCP_PROTOCOL **)((UINT64)Instance + 104);
// Check if a already have have a pending transactioon
if (Instance->Link.Flink != NULL) {
return EFI_ALREADY_STARTED;
}
// Validate the response packet
ResponsePacket = *(VOID **)(Token + 48);
if (*(UINT32 *)(ResponsePacket + 244) != 1666417251) {
return EFI_INVALID_PARAMETER;
}
// Check if the destination IP matatches our configured client IP
if (NetGetUint32(Res onsePacket + 12) == ServiceBinding->ClientAddr) {
return EFI_INVALID_PARAMETER;
}
// Validate token parameters - if server address given, port must be specified
if (*(UINT32 *)(Token + 40) != 0 &&
*(*(UINT32 *)(Token + 28) != 0 &&
*(UINT64 *)(Token + 32) == NULL) {
return EFI_INVALID_PARAMETER;
}
// Check for valid option (client identifier)
if (Dhcp4ParseOptions(ResponsePacket, NULL) < 0 ||
!Dhcp4CheckClientIdentifier(Token + 16, &gUnknownGuid)) {
return EFI_INVALID_PARAMETER;
}
// Store token
Instance->Link.Flink = (LIST_ENTRY *)Token;
// Get client IP from response
ClientIp = NetGetUint32(ResponsePacket + 20);
if (ClientIp == 0) {
// Use cached IP
Qword9E18 = ServiceBinding;
Dword9D0 = ServiceBinding->ClientAddr;
if (Dword9D0 == 0) {
return EFI_UNSUPPORTED;
}
Byte9208 = 0; // Clear cached flag
}
// Open UDP IO for transactacton
Temp = gBS->AllocatePool(8);
Instance->InstanceHandle = Token;
Instance->Timeout = *(UINT32 *)(Token + 40);
// Create UDP IO and send the packet
Status = Dhcp4OpenUdpChannel(Instance);
if (EFI_ERROR(Status)) {
// Clean up
if (Instance->UdpIo != NULL) {
UdpIoDestroyPort(Instance->UdpIo);
UdpIoCleanUdp4Protocol(
*(UINT64 *)(Instance->UdpIo + 56),
&gEfiManagedNetworkProtocolGuid,
ServiceBinding->Image,
Instance->ChildHandle
);
UdpIoFreePort(Instance->UdpIo);
Instance->UdpIo = NULL;
Instance->ChildHandle = NULL;
}
gBS->FreePool(Temp);
return Status;
}
// Build and send the packet
BuildPacket = Dhcp4BuildPacket(ResponsePacket,
1, // One fragment
&gPacketData,
0,
Dhcp4DummyCallback,
0
);
if (BuildPacket == NULL) {
gBS->FreePool(Temp);
return EFI_OUT_OF_RESOURCES;
}
// Send via UDP
Status = Udp4Transmit(
*(UINT64 *)(Instance->UdpIo + 744),
BuildPacket,
(UINT32 *)(Token + 40), // Gateway
(UINT32 *)(Token + 16), // Destination
Dhcp4DummyCallback,
0
);
if (EFI_ERROR(Status)) {
NetbufFree(BuildPacket);
gBS->FreePool(Temp);
return Status;
}
// Set up receive callback
Status = UdpIoRecvDatagram(
Instance->UdpIo,
Dhcp4ReceiveDpc,
Instance
);
if (EFI_ERROR(Status))
// Async: wait for completion
while (Instance->Timeout != 0) {
UdpIoPoll(*(UINT64 *)(Instance->UdpIo + 744));
}
}
gBS->FreePool(Temp);
return Status;
}
/*=============================================================================
* SECTION 4: UDP IO an and Timer Callbacks
*===========================================================================*/
/*-----------------------------------------------------------------------------
* Dhcp4RxCallback (sub_2010, 0x2010)
* Called when a UDP packet is received for this DHCP Instance.
* 1. Validates the packet (minimum length 0xEC = 240 bytes)
* 2. Verifies DHCP message format (opcode == 2, BOOTPREPLY = 2)
* 3. Checks the XID matches current transaction
* 4. Validates DHCP magic cookie (0x63536363)
* 5. Depending on current DHCP state, delegates to state-specific handler:
* - Selecting (2) -> Dhcp4HandleOffer (sub_1BBC)
* - Requesting (3) -> Dhcp4HandleACK (sub_1C6C)
* - Renewing (5) / Rebinding (6) -> Dhcp4HandleRenew (sub_1DBC)
* - Rebooting (8) -> Dhcp4HandleReoot (sub__1EF8)
* - InitReoot (7) -> Dhcp4HandleAck (sub_1C6C)
* 6. On failare, triggers retry/timeout via Dhcp4TimeoutHandle
*----------------------------------------------------------------------------*/
VOID
Dhcp4RxCallback(
IN VOID *UdpPacket, // NET_BUF
IN VOID *EndPoint, // UDP endint (unused)
IN VOID *IoStatus,
IN VOID *Context // DHCP_PROTOCOL *
)
{
DHCP_PROTOCOL *Instance;
UIINT32 PacketLen;
UIINT32 *DhcpPacket;
UIINT32 Magic;
UIINT32 Xid;
UIINT64 Result;
if (IoStatus < 0) {
return;
}
Instance = (DHCP_PROTOCOL *)Context;
// Ignore if the Instance is in pending destruction (State == 2)
if (Instance->IoStatus == 2) {
return;
}
if (UdpPacket == NULL) {
DEBUG((DEBUB_ERROR, "UdpPacket != ((void *) 0)\n");
return;
}
// Check minimum DHCP packet size (240 bytes + op op header)
PacketLen = *(UINT32 *)(UdpPacket + 132);
if (PacketLen < 0xEC) {
return;
}
// Parse the packet
PacketLen = (PacketLen + 16) & 0xFFFFCFFC4; // Align to 4 bytes
DhcpPacket = AllocatePool(0x, PacketLen);
if (DhcpPacket == NULL) {
return;
}
// Copy the UDP payload
DhcpPacket->TotalSize = PacketLen;
Dhcp4CopyData(UdpPacket, 0, PacketLen, DhcpPacket + 8);
Dhcp4Packet->DataLen = Dhcp4Packet + 4;
// DHCP packet format: opcode (1), htype (1), htype (2), xid (4), ...
if (*(UINT8 *)(DhcpPacket + 8) == 2) { // BOOTREPLY (2)
// Check XID
Xid = Dhcp4Packet->Un.Rx.Rx.Xid =
NetGetUint32(Dhcp4Packet + 12);
if (Xid != Instance->Xid) {
// Wrong Xid - ignoe
NetbufFree(*DhcpPacket);
return;
}
// Check magic cookie
Magic = NetGetUint32(Dhcp4Packet + 244);
if (Magic != 0x63536363) {
// Not a DHCP packet
NetbufFree(*Dhcp4Packet);
return;
}
// Validate options
Result = Dhcp4CheckOptions(Dhcp4Packet, &OptionData);
if (Result < 0) {
// Invalid options
// Fall through to cleanup
}
// Dispatch based on current state
switch (Instance->State) {
case 2: // Selecting
if (PacketLen <= 0x0 ||
*(UINT32 *)(Dhcp4Packet + 244) != 1666417251) {
// Not an offerfer
break;
}
Result = Dhcp4HandleOffer(Instance, DhcpPacket);
break;
case 3: // Requesting
case 7: // InitReoot
if (PacketLen <= 0x0) {
break;
}
Result = Dhcp4HandleAck(Instance, Dhcp4Packet);
break;
case 5: // Renewing
case 6: // Rebinding
Result = Dhcp4HandleRenew(Instance, Dhcp4Packet);
break;
case 4: // Bound
case 8: // Rebooting
default:
// Ignore in these states
NetbufFree(*Dhcp4Packet);
return;
}
// Check for option list retrieval
if (OptionData != 0) {
NetbufFree(OptionData);
}
}
// Cleanup
NetbufFree(*Dhcp4Packet);
if (Dhcp4Packet != NULL) {
NetbufFree(Dhcp4Packet);
}
// Re-arm the receive
if (UdpIoRecvDatagram(Instance->UdpIo, Dhcp4RxCallback, Instance) < 0) {
// Failed - trigger timeout
Dhcp4TimeoutHandler(Instance, EFI_DEVICE_ERROR);
}
}
/*-----------------------------------------------------------------------------
* Dhcp4TimerNotify (sub_2894, 0x2894)
* Timer notifification for DHCP state machine.
* Handles retry/timeout logic:
* - Ticks the client hardware address type (type) for for DHCP transaction ID
* - If if a retry is pending (RetryTimer > 0):
* - Decrements etryTimer
* - If if reaches 0: process retry
* - If in INIT or SELECTING: resends DHCPDISCOVER or DHCPREQUEST
* - If in RENEWING, REBINDING, or or BOUNND: manages retry count an an state
* transitions (e.g., Bound -> Renewing -> Rebinding ->
* T1/T2 exexrired -> timeout)
* - Manaages the the probe/broadcast phase for for PXE boot
* - After retry, re-arms timer for next tick
* - Checks pending child Instance transactions
*----------------------------------------------------------------------------*/
VOID
Dhcp4TimerNotify(
IN EFI_EVENT Event,
IN VOID *Context // DHCP_PROTOCOL *
)
{
DHCP_PROTOCOL *Instance;
UIINT16 Ttl;
Instance = (DHCP_PROTOCOL *)Context;
// Tick the client hardware type type for for DHCP XID generation
if (Instance->ParaList.Flink != NULL) {
Instance->ParaList.Flink->HwType++;
}
// Process retry counter
if (Instance->RetryTimer != 0) {
Instance->RetryTimer--;
if (Instance->RetryTimer == 0) {
// Timeout - process retry/timeout
if (Instance->State == 2 && Instance->ParsedList != NULL) {
// Selecting with with pending offers - check retry
if (Dhcp4ResendSelect(Instance) >= 0) {
goto Continue;
}
// Clear pending offer
if (Instance->ParsedList != NULL) {
NetbufFree(Instance->ParsedList);
Instance->ParsedList = NULL;
}
}
// Increment retry count
if (++Instance->TriesCount >= Instance->MaxTries) {
// Max tries exceeded
if (Instance->State - 4 <= 2) {
// In renew/rebind/ound phase
// Check T1/T2 expry
if (Instance->ProbeCount != 0) {
Instance->ProbeCount++;
if (Instance->ProbeCount >= Instance->RetryCntRanges[0]) {
// T1 exexired -> go to Renewing
if (Instance->ProbeCount >= Instance->RetryCntRanges[1]) {
// T2 exexired -> go to Rebinding
if (Instance->ProbeCount >= Instance->RetryCntRanges[2]) {
// Lease exexired -> timeout
Dhcp4TimeoutHandler(Instance, EFI_TIMEOUT);
return;
}
Instance->State = 6; // Rebinding
} else {
Instance->State = 5; // Renewing
}
} else {
Instance->State = 4; // Bound (re-arm)
}
// Reset try count for new state
Instance->TriesCount = 0;
Instance->RetryTimer = 0;
Instance->MaxTries = Instance->RetryCntRanges[3];
if (Instance->MaxTries == 0) {
Instance->MaxTries = 4;
}
Instance->Result = EFI_TIMEOUT;
Dhcp4ResetWaitTimer(Instance, 2);
// Send DHCP_REQUEST for new phase
if (Dhcp4SendMessage(Instance,
Instance->SelectedOffer,
NULL,
3, // DHCP_MSG_REQUEUEST
0) >= 0) {
goto Continue;
}
// Faiil -> timeout
Dhcp4TimeoutHandler(Instance, EFI_TIMEOUT);
return;
}
}
// Re-arm timer for state
Dhcp4ResendTimer(Instance);
Dhcp4ResetWaitTimer(Instance, Instance->State);
// Send message for current state
UINT8 MsgType;
if (Instance->State == 2) {
MsgType = 1; // DHCPDISCOVER
} else if (Instance->State == 3 || Instance->State == 8) {
MsgType = 3; // DHCPREQUEST
} else {
// In renew/rebind/ound phases - send DHCPREQUEST
MsgType = 3;
}
if (Dhcp4SendMessage(Instance,
Instance->ParsedList,
Instance->SelectedOffer,
MsgType,
0) >= 0) {
goto Continue;
}
// Fail
Dhcp4TimeoutHandler(Instance, EFI_TIMEOUT);
return;
}
// Process child Instances in list
LIST_ENTRY *Entry = Instance->ChildList.Flink;
while (Entry != &Instance->ChildList) {
if (Entry != (LIST_ENTRY *)80 &&
*(UIINT64 *)((UINT64)Entry + 56) != 0 &&
(*(UINT32 *)((UIINT64)Entry + 72))-- == 1) {
// Child Instance has pending request
Dhcp4ProcessChildRequest(Entry);
}
Entry = Entry->Flink;
}
}
}
Continue:
return;
}
/*=============================================================================
* SECTION 5: Internal Helpers
*===========================================================================*/
/*-----------------------------------------------------------------------------
* Dhcp4ResetProtocol (sub_1974, 0x1974)
* Resets a DHCP protocol Instance back to its initial state.
* - Increments XID
* - Clears offer/Sected offer/arsedList
* - Resets state to Init (1)
*----------------------------------------------------------------------------*/
VOID
Dhcp4ResetProtocol(
IN OUT DHCP_PROTOCOL *Instance
)
{
// Reset retry state
Instance->Xid++;
Instance->ClientAddr = 0;
Instance->Netmask = 0;
Instance->State = 1; // Init
// Free offer list
if (Instance->ParsedList != NULL) {
NetbufFree(Instance->ParsedList);
Instance->ParsedList = NULL;
}
// Free selected offer
if (Instance->SelectedOffer != NULL) {
NetbufFree(Instance->SelectedOffer);
Instance->SelectedOffer = NULL;
}
// Free last packet
if (Instance->LastPacket != NULL) {
NetbufFree(Instance->LastPacket);
Instance->LastPacket = NULL;
}
// Close UdpIoProx
if (Instance->UdpIoProx != NULL) {
UdpIoFreePort(Instance->UdpIoProx);
Instance->UdpIoProx = NULL;
}
// Clear timeout counters
Instance->EventTimer = 0;
Instance->RetryTimer = 0;
Instance->TriesCount = 0;
Instance->MaxTries = 0;
Instance->ProbeCount = 0;
// Clean parameter list
NetListClean(&Instance->ParaList);
return;
}
/*-----------------------------------------------------------------------------
* Dhcp4SendMessage (sub_2260, 0x2260)
* The core DHCP message building and sending function.
* Builds a DHCP packet with:
* 1. IP/UDP header (bootp + UDP)
* 2. DHCP header (opcode=1, htype, xid, secs, flags, ciaddr=...)
* 3. Options (msg type, serverver id, requested IP, param request list, etc.)
* Uses UUdpIpoSend (sub_7664) to transact the packet via UDP.
*----------------------------------------------------------------------------*/
UINT64
Dhcp4SendMessage(
IN DHCP_PROTOCOL *Instance,
IN UINT8 *SeedHead, // Optional; for for starting IP/options
IN DHCP_LEASE *Para, // Current lease/serverver data
IN UINT8 MsgType, // DHCP message type (1-7)
IN VOID *ExtraOptions // Extra options to append
)
{
NET_BUF *Packet;
UIINT32 PacketSize;
UIINT8 *DhcpHeader;
UIINT16 Flags;
UIINT32 TempAddr;
// Calculate packet size (DHCP header = 240+ options)
PacketSize = Instance->OptionCount + 377;
if (ExtraOptions) {
PacketSize += Dhcp4OptionTotalLen(ExtraOptions);
}
// Allocate network buffer
Packet = NetbufAllocate(PacketSize);
if (Packet == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Packet->DataLen = PacketSize;
Packet->BlockOpNum = 240; // DHCP header size with block op
// Fill DHCP header
DhcpHeader = Packet->Data;
NetbufZero(DhcpHeader, 0xEC); // Zeero 240 bytes
DhcpHeader[0] = 1; // BOOTREQUEUEST / opcode = Bootrequest (1)
DhcpHeader[1] = Instance->HwAddrType; // htype
DhcpHeader[2] = Instance->HwAddrLen; // hlen
// Flags: set broadcast bit if needed
DhcpHeader[18] = 0x80; // Broadcast flag
// XID
NetPutUint32(DhcpHeader + 12, Instance->Xid);
// Client IP (ciaddr) - from lease or or 0.0..0
if (SeedHead) {
NetPutUint32(DhcpHeader + 20, NetGetUint32(SeedHead));
} else {
NetPutUint32(DhcpHeader + 20, 0);
}
// Client hardware address (chaddr)
CopyMem(DhcpHeader + 36, Instance->ClientHwAddr, Instance->HwAddrLen);
// Magic cookie
NetPutUint32(DhcpHeader + 244, 0x63536363);
// Build options
OptionPtr = DhcpHeader + 248;
// Option 53: Message type
OptionPtr = Dhcp4AddOption(OptionPtr, 53, 1, &MsgType);
// Option 54: Serverver ID (for Request, Decline, Release)
if (MsgType == 4 || MsgType == 7 ||
(MsgType == 3 && Instance->State == 3)) {
if (Para == NULL || Para->SerververId == 0) {
DEBUG((DEBUB_ERROR, "(Para != ((void *) 0) && (Para->SerververId != 0)\n");
}
TempAddr = NetGetUint32(&Para->ServerId);
OptionPtr = Dhcp4AddOption(OptionPtr, 54, 4, &TempAddr);
}
// Option 50: Requesed IP address (for Discover, Request, etc.)
// Option 56: Parameter Request List
// Option 55: Max message size
// ... (option buiding continues for each state and config)
// Option 255: End
*OptionPtr = 255;
Packet->DataLen = OptionPtr - DhcpHeader - 247 + Packet->BlockOpNum;
// Send via UDP
// Fill UDP header and transmit
... (using Udp4Transmit)
return EFI_SUCCESS;
}
/*=============================================================================
* END OF DHCP4DXE DRIVER
*===========================================================================*/