从 efi 应用程序发送 TCP 或 UDP 数据包

Send TCP or UDP packets from efi application

我想开发一个在 EFI shell 中从 startup.nsh 自动执行的应用程序。这个应用程序应该将原始字节发送到一个 ip 地址并接收一些回来。我到处寻找在我的代码中实现简单网络协议的解释和示例,但一无所获。有人可以使用 gnu_efi 库解释和展示代码示例吗?

这是一个如何使用 EDK2 发送和接收 UDP 数据包的示例,将其移植到 gnu-efi 应该是一个简单的任务,用 uefi_call_wrapper 包装所有 gBS->、gRT-> 和 protocolXY 调用.

更改全局值以匹配您的客户端和服务器。

#include <Uefi.h>
#include <Library\UefiLib.h>
#include <Protocol\ServiceBinding.h>
#include <Protocol\Udp4.h>
#include <Protocol\SimpleNetwork.h>
#include <Protocol\ManagedNetwork.h>
#include <Protocol\Ip4.h>

#ifndef LOG
#define LOG(fmt, ...) AsciiPrint(fmt, __VA_ARGS__)
#endif

#ifndef TRACE
#define TRACE(status)   LOG("Status: '%r', Function: '%a', File: '%a', Line: '%d'\r\n", status, __FUNCTION__, __FILE__, __LINE__)
#endif

static EFI_GUID gEfiUdp4ServiceBindingProtocolGuid = EFI_UDP4_SERVICE_BINDING_PROTOCOL_GUID;
static EFI_GUID gEfiUdp4ProtocolGuid = EFI_UDP4_PROTOCOL_GUID;

extern EFI_BOOT_SERVICES    *gBS;
extern EFI_RUNTIME_SERVICES *gRT;

static BOOLEAN gTransmitCompleteFlag = FALSE;
static BOOLEAN gReceiveCompleteFlag = FALSE;

/*
Configuration
*/
static EFI_IPv4_ADDRESS gLocalAddress = { 10, 0, 2, 200 };
static EFI_IPv4_ADDRESS gSubnetMask = { 255, 255, 255, 0 };
static UINT16 gLocalPort = 0;

static EFI_IPv4_ADDRESS gRemoteAddress = { 10, 0, 2, 180 };
static UINT16 gRemotePort = 4444;


static VOID 
EFIAPI 
TransmitEventCallback(
    IN  EFI_EVENT   Event,
    IN  void        *UserData)
{
    gTransmitCompleteFlag = TRUE;
}

static VOID
EFIAPI
ReceiveEventCallback(
    IN  EFI_EVENT   Event,
    IN  void        *UserData)
{
    gReceiveCompleteFlag = TRUE;
}

static EFI_STATUS
EFIAPI
WaitForFlag(
    IN  BOOLEAN             *Flag,
    IN  EFI_UDP4_PROTOCOL   *Udp4Protocol   OPTIONAL,
    IN  UINTN               Timeout)
{
    EFI_STATUS  Status;
    UINT8       LastSecond = MAX_UINT8;
    UINT8       Timer = 0;
    EFI_TIME    CurrentTime;

    while (!*Flag && (Timeout == 0 || Timer < Timeout)) {
        if (Udp4Protocol) {
            Udp4Protocol->Poll(
                Udp4Protocol);
        }

        // use gRT->GetTime to exit this loop
        Status = gRT->GetTime(&CurrentTime, NULL);

        if (EFI_ERROR(Status)) {
            TRACE(Status);
            // Error handling
            return Status;
        }

        if (LastSecond != CurrentTime.Second) {
            LastSecond = CurrentTime.Second;
            Timer++;
        }
    }

    return *Flag ? EFI_SUCCESS : EFI_TIMEOUT;
}

EFI_STATUS
EFIAPI
UefiMain(
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable)
{
    EFI_STATUS                      Status;
    
    EFI_UDP4_CONFIG_DATA            Udp4ConfigData;

    EFI_UDP4_COMPLETION_TOKEN       Udp4ReceiveCompletionToken;
    EFI_UDP4_COMPLETION_TOKEN       Udp4TansmitCompletionToken;
    EFI_UDP4_TRANSMIT_DATA          Udp4TransmitData;

    EFI_HANDLE                      Udp4ChildHandle = NULL;

    EFI_UDP4_PROTOCOL               *Udp4Protocol = NULL;
    EFI_SERVICE_BINDING_PROTOCOL    *Udp4ServiceBindingProtocol = NULL;

    CHAR8                           TxBuffer[] = "Hello Server!";


    /*
    Step 1: Locate the corresponding Service Binding Protocol, if there is more then 1 network interface gBS->LocateHandleBuffer should be used
    */

    Status = gBS->LocateProtocol(
        &gEfiUdp4ServiceBindingProtocolGuid,
        NULL,
        &Udp4ServiceBindingProtocol);

    if (EFI_ERROR(Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    /*
    Step 2: Create a new UDP4 instance
    */

    Status = Udp4ServiceBindingProtocol->CreateChild(
        Udp4ServiceBindingProtocol,
        &Udp4ChildHandle);

    if (EFI_ERROR(Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    Status = gBS->HandleProtocol(
        Udp4ChildHandle,
        &gEfiUdp4ProtocolGuid,
        &Udp4Protocol);

    if (EFI_ERROR(Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    /*
    Step 3: Prepare the UDP4 instance
    */

    Udp4ConfigData.AcceptBroadcast = FALSE;
    Udp4ConfigData.AcceptPromiscuous = FALSE;
    Udp4ConfigData.AcceptAnyPort = FALSE;
    Udp4ConfigData.AllowDuplicatePort = FALSE;

    Udp4ConfigData.TimeToLive = 16;
    Udp4ConfigData.TypeOfService = 0;
    Udp4ConfigData.DoNotFragment = TRUE;
    Udp4ConfigData.ReceiveTimeout = 0;
    Udp4ConfigData.TransmitTimeout = 0;

    // Change to TRUE and set the following fields to zero if DHCP is used
    Udp4ConfigData.UseDefaultAddress = FALSE;
    gBS->CopyMem(&Udp4ConfigData.StationAddress, &gLocalAddress, sizeof(Udp4ConfigData.StationAddress));
    gBS->CopyMem(&Udp4ConfigData.SubnetMask, &gSubnetMask, sizeof(Udp4ConfigData.SubnetMask));
    Udp4ConfigData.StationPort = gLocalPort;
    gBS->CopyMem(&Udp4ConfigData.RemoteAddress, &gRemoteAddress, sizeof(Udp4ConfigData.RemoteAddress));
    Udp4ConfigData.RemotePort = gRemotePort;

    Status = Udp4Protocol->Configure(
        Udp4Protocol,
        &Udp4ConfigData);

    if (EFI_ERROR(Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    /*
    Step 4: Send data and wait for completion
    */

    Udp4TansmitCompletionToken.Status = EFI_SUCCESS;
    Udp4TansmitCompletionToken.Event = NULL;

    Status = gBS->CreateEvent(
            EVT_NOTIFY_SIGNAL,
            TPL_CALLBACK,
            TransmitEventCallback,
            NULL,
            &(Udp4TansmitCompletionToken.Event));

    if (EFI_ERROR(Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }
        
    Udp4TansmitCompletionToken.Packet.TxData = &Udp4TransmitData;

    Udp4TransmitData.UdpSessionData = NULL;
    gBS->SetMem(&Udp4TransmitData.GatewayAddress, sizeof(Udp4TransmitData.GatewayAddress), 0x00);
    Udp4TransmitData.DataLength = sizeof(TxBuffer);
    Udp4TransmitData.FragmentCount = 1;
    Udp4TransmitData.FragmentTable[0].FragmentLength = Udp4TransmitData.DataLength;
    Udp4TransmitData.FragmentTable[0].FragmentBuffer = TxBuffer;

    gTransmitCompleteFlag = FALSE;

    LOG("Sending data...\r\n");

    Status = Udp4Protocol->Transmit(
        Udp4Protocol,
        &Udp4TansmitCompletionToken);

    if (EFI_ERROR(Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    Status = WaitForFlag(
        &gTransmitCompleteFlag,
        Udp4Protocol,
        10);

    if (EFI_ERROR(Status)) {
        TRACE(EFI_TIMEOUT);
        // Error handling
        return EFI_TIMEOUT;
    }

    if (EFI_ERROR(Udp4TansmitCompletionToken.Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    LOG("Data sent.\r\n");
    
    /*
    Step 5: Receive data
    */
    
    Udp4ReceiveCompletionToken.Status = EFI_SUCCESS;
    Udp4ReceiveCompletionToken.Event = NULL;

    Status = gBS->CreateEvent(
        EVT_NOTIFY_SIGNAL,
        TPL_CALLBACK,
        ReceiveEventCallback,
        NULL,
        &(Udp4ReceiveCompletionToken.Event));

    if (EFI_ERROR(Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    Udp4ReceiveCompletionToken.Packet.RxData = NULL;

    gReceiveCompleteFlag = FALSE;

    LOG("Receiving data...\r\n");

    Status = Udp4Protocol->Receive(
        Udp4Protocol,
        &Udp4ReceiveCompletionToken);

    if (EFI_ERROR(Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    Status = WaitForFlag(
        &gReceiveCompleteFlag,
        Udp4Protocol,
        10);

    if (EFI_ERROR(Status)) {
        TRACE(EFI_TIMEOUT);
        // Error handling
        return EFI_TIMEOUT;
    }

    if (EFI_ERROR(Udp4ReceiveCompletionToken.Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }
    
    /*
    Step 6: Process received data
    */
    
    if (
        Udp4ReceiveCompletionToken.Packet.RxData &&
        Udp4ReceiveCompletionToken.Packet.RxData->FragmentCount > 0 &&
        Udp4ReceiveCompletionToken.Packet.RxData->DataLength > 0) {

        LOG("Received '%a'.\r\n", 
            Udp4ReceiveCompletionToken.Packet.RxData->FragmentTable[0].FragmentBuffer);
    }
    else {
        LOG("Received an empty package.\r\n");
    }
    
    /*
    Step 7: Cleanup
    */

    if (
        Udp4ReceiveCompletionToken.Packet.RxData &&
        Udp4ReceiveCompletionToken.Packet.RxData->RecycleSignal) {

        Status = gBS->SignalEvent(Udp4ReceiveCompletionToken.Packet.RxData->RecycleSignal);

        if (EFI_ERROR(Udp4ReceiveCompletionToken.Status)) {
            TRACE(Status);
            // Error handling
            return Status;
        }
    }

    Status = Udp4ServiceBindingProtocol->DestroyChild(
        Udp4ServiceBindingProtocol,
        Udp4ChildHandle);

    if (EFI_ERROR(Udp4ReceiveCompletionToken.Status)) {
        TRACE(Status);
        // Error handling
        return Status;
    }

    return EFI_SUCCESS;
}