0

I want to develop an application to be automatically executed from startup.nsh in EFI shell. This application should send raw bytes to an ip address and receive some back. I looked everywhere for explanation and example for the implementation of simple network protocol in my code but found nothin. Can someone explain and show a code example using gnu_efi libaries?

  • The UEFI specification contains a complete sample using the HTTP protocol. The steps are nearly the same for UDP and TCP (call Connect before sending any data if you use TCP). – MiSimon Apr 12 '21 at 06:12
  • I dont need to use the http protocol just the simple network protocol for sending a simple char and receive one back. I read about handle protocol but I can’t understand how to actually implement all these things together – Raffaele Bertani Apr 13 '21 at 07:02
  • The title says "TCP or UDP" you need the TCPv4 or the UDP protocol to do that, the SNP (simple network protocol) should not be used directly, you should use the MNP instead. But you can only send raw packets with either SNP or MNP. – MiSimon Apr 13 '21 at 07:22
  • Thanks a lot. This UDPv4 PROTOCOL looks like the solution. But the main problem remains the same. Could you or someone provide a few lines of example to send a character “S” to a x.y.w.z:33333? – Raffaele Bertani Apr 13 '21 at 10:10
  • I can provide you a simple sample based on EDK2, you just need to use the uefi_call_wrapper around every UEFI method to make it run with gnu-efi. – MiSimon Apr 13 '21 at 10:53
  • https://stackoverflow.com/questions/67073808/writing-my-first-efi-application-with-gnu-efi This question explain better my situation. Thanks a lot – Raffaele Bertani Apr 13 '21 at 11:20
  • @MiSimon here an error main.c:284:58: error: request for member ‘DestroyChild’ in something not a structure or union 284 | Status = uefi_call_wrapper(Udp4ServiceBindingProtocol->DestroyChild,2,Udp4ServiceBindingProtocol,Udp4ChildHandle); – Raffaele Bertani Apr 15 '21 at 12:20
  • Please create a github (or similar) project and post the link. – MiSimon Apr 15 '21 at 14:39
  • I forgot the InitializeLib. Tonight I’m going to try again if it still won’t work I’ll create a GitHub – Raffaele Bertani Apr 15 '21 at 16:32
  • @MiSimon here's my project: https://github.com/RapRaf/AlexaBootEFI – Raffaele Bertani Apr 15 '21 at 20:29
  • @MiSimon sorry, it sends nothin. It executes and exits without listening for the reply. No errors but no packets sent from the efi application. What could it be? – Raffaele Bertani Apr 20 '21 at 13:23

1 Answers1

0

Here is a sample how to send and receive UDP packets with EDK2, porting it to gnu-efi should be an easy task, wrap all gBS->, gRT-> and protocolXY calls with the uefi_call_wrapper.

Change the global values to match your client and server.

#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;
}
MiSimon
  • 1,225
  • 1
  • 8
  • 10
  • Thank you again! I’m going to contact you again to show you the ultimated work (if I’ll be able to finish it) – Raffaele Bertani Apr 13 '21 at 15:36
  • I keep getting these errors. Do you know why? In file included from /usr/include/efi/efi.h:57, from main.c:1: /usr/include/efi/efiudp.h:14:5: error: expected declaration specifiers or ‘...’ before ‘{’ token 14 | { 0x3ad9df29, 0x4501, 0x478d, {0xb1, 0xf8, 0x7f, 0x7f, 0xe7, 0x0e, 0x50, 0xf3} } – Raffaele Bertani Apr 15 '21 at 12:08