Отправка пакетов TCP или UDP из приложения efi

Я хочу разработать приложение для автоматического запуска из startup.nsh в оболочке EFI. Это приложение должно отправлять необработанные байты на IP-адрес и получать обратно. Я везде искал объяснение и пример реализации простого сетевого протокола в моем коде, но ничего не нашел. Может ли кто-нибудь объяснить и показать пример кода с использованием библиотек gnu_efi?


person Raffaele Bertani    schedule 11.04.2021    source источник
comment
Спецификация UEFI содержит полный пример использования протокола HTTP. Шаги почти одинаковы для UDP и TCP (вызовите Connect перед отправкой каких-либо данных, если вы используете TCP).   -  person MiSimon    schedule 12.04.2021
comment
Мне не нужно использовать протокол http, просто простой сетевой протокол для отправки простого символа и получения его обратно. Я читал о протоколе дескриптора, но я не могу понять, как на самом деле реализовать все эти вещи вместе.   -  person Raffaele Bertani    schedule 13.04.2021
comment
В заголовке указано, что для TCP или UDP вам нужен протокол TCPv4 или UDP, SNP (простой сетевой протокол) не следует использовать напрямую, вместо этого вы должны использовать MNP. Но вы можете отправлять необработанные пакеты только с SNP или MNP.   -  person MiSimon    schedule 13.04.2021
comment
Большое спасибо. Этот ПРОТОКОЛ UDPv4 выглядит как решение. Но главная проблема остается прежней. Не могли бы вы или кто-нибудь привести несколько строк примера отправки символа «S» на x.y.w.z:33333?   -  person Raffaele Bertani    schedule 13.04.2021
comment
Я могу предоставить вам простой пример на основе EDK2, вам просто нужно использовать uefi_call_wrapper вокруг каждого метода UEFI, чтобы заставить его работать с gnu-efi.   -  person MiSimon    schedule 13.04.2021
comment
stackoverflow.com/ questions/67073808/ Этот вопрос лучше объясняет мою ситуацию. Большое спасибо   -  person Raffaele Bertani    schedule 13.04.2021
comment
@MiSimon здесь ошибка main.c: 284: 58: ошибка: запрос члена «DestroyChild» в чем-то, что не является структурой или союзом 284 | Status = uefi_call_wrapper(Udp4ServiceBindingProtocol->DestroyChild,2,Udp4ServiceBindingProtocol,Udp4ChildHandle);   -  person Raffaele Bertani    schedule 15.04.2021
comment
Пожалуйста, создайте проект на github (или аналогичный) и опубликуйте ссылку.   -  person MiSimon    schedule 15.04.2021
comment
Я забыл InitializeLib. Сегодня вечером я попробую еще раз, если это все еще не сработает, я создам GitHub   -  person Raffaele Bertani    schedule 15.04.2021
comment
@MiSimon, вот мой проект: github.com/RapRaf/AlexaBootEFI   -  person Raffaele Bertani    schedule 15.04.2021
comment
@MiSimon извините, ничего не отправляет. Он выполняется и завершается, не дождавшись ответа. Нет ошибок, но нет пакетов, отправленных из приложения efi. Что бы это могло быть?   -  person Raffaele Bertani    schedule 20.04.2021


Ответы (1)


Вот пример того, как отправлять и получать пакеты UDP с помощью EDK2, перенос его на gnu-efi должен быть простой задачей, оберните все вызовы gBS-›, gRT-› и protocolXY с помощью uefi_call_wrapper.

Измените глобальные значения, чтобы они соответствовали вашему клиенту и серверу.

#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;
}
person MiSimon    schedule 13.04.2021
comment
Еще раз спасибо! Я свяжусь с вами снова, чтобы показать вам окончательную работу (если я смогу ее закончить) - person Raffaele Bertani; 13.04.2021
comment
Я продолжаю получать эти ошибки. Ты знаешь почему? В файле, включенном из /usr/include/efi/efi.h:57, из main.c:1: /usr/include/efi/efiudp.h:14:5: ошибка: ожидаемые спецификаторы объявления или '...' перед токеном '{' 14 | { 0x3ad9df29, 0x4501, 0x478d, {0xb1, 0xf8, 0x7f, 0x7f, 0xe7, 0x0e, 0x50, 0xf3} } - person Raffaele Bertani; 15.04.2021