Большинство протоколов UEFI считаются "неподдерживаемыми"

Я пишу исполняемый файл EFI на устройстве SoC (Up Board), чтобы помочь нам автоматизировать обновления BIOS и загрузки PXE для установки нашего программного обеспечения на многочисленные устройства.

Проблема, с которой я столкнулся, заключается в том, что, по-видимому, большинство протоколов в спецификации "не поддерживаются" на этой платформе, даже базовые задачи файловой системы. Единственное, что я успешно использовал, это LOADED_IMAGE_PROTOCOL. Я использую gnu-efi для компиляции кода, основывая свой код на этом примере https://mjg59.dreamwidth.org/18773.html. Что-то я не так делаю, или это просто прошивка, абсолютно не реализующая ни один из протоколов?

Американская утилита Megatrends "AfuEfix64.efi" способна извлекать информацию BIOS, а обновления SoC BIOS выполняются с помощью исполняемого файла Intel EFI. Оба закрыты, к сожалению. Моя первоначальная идея состояла в том, чтобы написать сценарий, который анализирует выходные данные этих исполняемых файлов, но я не думаю, что оболочка EFI имеет много функций для задач такого типа, поэтому я перешел к написанию исполняемого файла EFI для выполнения этого.

Минимальный код, показывающий это:

#include <efi.h>
#include <efilib.h>
#include <x86_64/efibind.h>

// gnu-efi does not currently define firmware management
// https://raw.githubusercontent.com/tianocore/edk2/master/MdePkg/Include/Protocol/FirmwareManagement.h
#include "efifirmware.h"

// gnu-efi does not currently define this
#define EFI_LOAD_FILE2_PROTOCOL_GUID \
{ 0x4006c0c1, 0xfcb3, 0x403e, \
{ 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d }}
typedef EFI_LOAD_FILE_PROTOCOL EFI_LOAD_FILE2_PROTOCOL;

void tryProtocol(EFI_GUID proto_guid, void** out, const CHAR16* name,
    EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {

    EFI_STATUS status;
    status = uefi_call_wrapper(systemTable->BootServices->HandleProtocol, 3,
        imageHandle, &proto_guid, out);

    if (EFI_ERROR(status)) {
        Print(L"HandleProtocol error for %s: %r\n", name, status);
    } else {
        Print(L"Protocol %s is supported\n", name);
    }
}

void tryProtocols(EFI_HANDLE imgh, EFI_SYSTEM_TABLE* syst) {
    EFI_LOADED_IMAGE* loaded_image = NULL;
    EFI_GUID guid_imgprot = LOADED_IMAGE_PROTOCOL;
    tryProtocol(guid_imgprot, (void**)&loaded_image,
        L"LOADED_IMAGE_PROTOCOL", imgh, syst);

    Print(L"Image base: %lx\n", loaded_image->ImageBase);
    Print(L"Image size: %lx\n", loaded_image->ImageSize);
    Print(L"Image file: %s\n", DevicePathToStr(loaded_image->FilePath));

    EFI_FIRMWARE_MANAGEMENT_PROTOCOL* fw_manage = NULL;
    EFI_GUID guid_fwman_prot = EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
    tryProtocol(guid_fwman_prot, (void**)&fw_manage,
        L"FIRMWARE_MANAGEMENT_PROTOCOL", imgh, syst);

    EFI_LOAD_FILE_PROTOCOL* load_file = NULL;
    EFI_GUID guid_loadf_prot = EFI_LOAD_FILE_PROTOCOL_GUID;
    tryProtocol(guid_loadf_prot, (void**)&load_file,
        L"LOAD_FILE_PROTOCOL", imgh, syst);

    EFI_LOAD_FILE2_PROTOCOL* load_file2 = NULL;
    EFI_GUID guid_loadf2_prot = EFI_LOAD_FILE2_PROTOCOL_GUID;
    tryProtocol(guid_loadf2_prot, (void**)&load_file2,
        L"LOAD_FILE2_PROTOCOL", imgh, syst);

    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* simple_fs = NULL;
    EFI_GUID guid_simple_fs_prot = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
    tryProtocol(guid_simple_fs_prot, (void**)&simple_fs,
        L"SIMPLE_FILE_SYSTEM_PROTOCOL", imgh, syst);

    EFI_DISK_IO_PROTOCOL* disk = NULL;
    EFI_GUID guid_disk_io_prot = EFI_DISK_IO_PROTOCOL_GUID;
    tryProtocol(guid_disk_io_prot, (void**)&disk,
        L"DISK_IO_PROTOCOL", imgh, syst);

    EFI_BLOCK_IO_PROTOCOL* block = NULL;
    EFI_GUID guid_block_io_prot = EFI_BLOCK_IO_PROTOCOL_GUID;
    tryProtocol(guid_block_io_prot, (void**)&block,
        L"BLOCK_IO_PROTOCOL", imgh, syst);
}

EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
    InitializeLib(imageHandle, systemTable);
    Print(L"Image loaded\n");

    tryProtocols(imageHandle, systemTable);

    return EFI_SUCCESS;
}

Вот вывод для его запуска:

EFI Shell version 2.40 [5.11]
Current running mode 1.1.2
Device mapping table
  fs0  :HardDisk - Alias hd6b blk0
        PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(1,GPT,2DCDDADD-8F3A-4A77-94A9-010A8C700BB8,0x800,0x100000)
  fs1  :Removable HardDisk - Alias hd9g0a0b blk1
        PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)/HD(1,MBR,0x528E6A1F,0x800,0x1CDE800)
  blk0 :HardDisk - Alias hd6b fs0
        PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(1,GPT,2DCDDADD-8F3A-4A77-94A9-010A8C700BB8,0x800,0x100000)
  blk1 :Removable HardDisk - Alias hd9g0a0b fs1
        PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)/HD(1,MBR,0x528E6A1F,0x800,0x1CDE800)
  blk2 :HardDisk - Alias (null)
        PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(2,GPT,8AC0F94E-3CA2-4C03-BE00-3A69721CC391,0x100800,0x1C1F7DF)
  blk3 :BlockDevice - Alias (null)
        PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)
  blk4 :BlockDevice - Alias (null)
        PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x1)
  blk5 :BlockDevice - Alias (null)
        PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x2)
  blk6 :Removable BlockDevice - Alias (null)
        PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)

Press ESC in 4 seconds to skip startup.nsh, any other key to continue.

fs1:\> tryprotocols.efi
Image loaded
Protocol LOADED_IMAGE_PROTOCOL is supported
Image base: 55BA6000
Image size: F000
Image file: \/tryprotocols.efi
HandleProtocol error for FIRMWARE_MANAGEMENT_PROTOCOL: Unsupported
HandleProtocol error for LOAD_FILE_PROTOCOL: Unsupported
HandleProtocol error for LOAD_FILE2_PROTOCOL: Unsupported
HandleProtocol error for SIMPLE_FILE_SYSTEM_PROTOCOL: Unsupported
HandleProtocol error for DISK_IO_PROTOCOL: Unsupported
HandleProtocol error for BLOCK_IO_PROTOCOL: Unsupported

fs1:\>

А вот Makefile, который я использую для его компиляции:

ARCH=x86_64
OBJS=tryprotocols.o
TARGET=tryprotocols.efi
TARGET_SO=$(TARGET:.efi=.so)
GNUEFIDIR=/home/gekko/gnu-efi-3.0.8
EFIINC=$(GNUEFIDIR)/inc
EFIINCS=-I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
LIB=$(GNUEFIDIR)/$(ARCH)/lib
EFILIB=$(GNUEFIDIR)/gnuefi
EFI_CRT_OBJS=$(GNUEFIDIR)/$(ARCH)/gnuefi/crt0-efi-$(ARCH).o
EFI_LDS=$(EFILIB)/elf_$(ARCH)_efi.lds

CFLAGS=$(EFIINCS) -fno-stack-protector -fpic \
    -fshort-wchar -mno-red-zone -Wall

ifeq ($(ARCH),x86_64)
    CFLAGS += -DEFI_FUNCTION_WRAPPER
endif

LDFLAGS=-nostdlib -znocombreloc -T $(EFI_LDS) -shared \
    -Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS)

all: $(TARGET)

$(TARGET_SO): $(OBJS)
    ld $(LDFLAGS) $(OBJS) -o $@ -lefi -lgnuefi

%.efi: %.so
    objcopy -j .text -j .sdata -j .data -j .dynamic \
    -j .dynsym  -j .rel -j .rela -j .reloc \
    --target=efi-app-$(ARCH) $^ $@

clean:
    rm -f *.o
    rm -f $(TARGET)
    rm -f $(TARGET_SO)

РЕДАКТИРОВАТЬ: модифицированная версия, которая правильно использует LocateProtocol() для поиска протоколов и использует их для открытия и закрытия файла.

#include <efi.h>
#include <efilib.h>
#include <x86_64/efibind.h>

BOOLEAN tryProtocol(EFI_GUID proto_guid, void** out, const CHAR16* name,
    EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {

    *out = NULL;
    EFI_STATUS status;
    EFI_HANDLE interface = NULL;

    status = uefi_call_wrapper(systemTable->BootServices->LocateProtocol, 3,
        &proto_guid, NULL, &interface);

    if (EFI_ERROR(status)) {
        Print(L"LocateProtocol error for %s: %r\n", name, status);
        return FALSE;
    }

    Print(L"Locate protocol address: %s, %x\n", name, interface);
    *out = interface;

    return TRUE;
}
EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
    InitializeLib(imageHandle, systemTable);
    Print(L"Image loaded\n");

    EFI_LOADED_IMAGE* loaded_image = NULL;
    EFI_GUID guid_imgprot = LOADED_IMAGE_PROTOCOL;

    if (tryProtocol(guid_imgprot, (void**)&loaded_image,
        L"LOADED_IMAGE_PROTOCOL", imageHandle, systemTable) != TRUE) {
        Print(L"Missing required protocol. Aborting\n");
        return EFI_SUCCESS;
    }

    Print(L"Image base: %lx\n", loaded_image->ImageBase);
    Print(L"Image size: %lx\n", loaded_image->ImageSize);
    Print(L"Image file: %s\n", DevicePathToStr(loaded_image->FilePath));

    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* simple_fs = NULL;
    EFI_GUID guid_simple_fs_prot = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;

    if (tryProtocol(guid_simple_fs_prot, (void**)&simple_fs,
        L"EFI_SIMPLE_FILE_SYSTEM_PROTOCOL", imageHandle, systemTable) != TRUE) {
        Print(L"Missing required protocol. Aborting\n");
        return EFI_SUCCESS;
    }

    EFI_FILE_PROTOCOL* vol_proto = NULL;
    EFI_STATUS status;

    Print(L"dereffing\n");
    Print(L"address of OpenVolume: %x\n", simple_fs->OpenVolume);

    status = uefi_call_wrapper(simple_fs->OpenVolume, 2, simple_fs, &vol_proto);

    if (EFI_ERROR(status)) {
        Print(L"Error opening volume: %r\n", status);
        return EFI_SUCCESS;
    }

    Print(L"SIMPLE_FILE_SYSTEM volume opened\n");

    EFI_FILE_PROTOCOL* f;
    CHAR16 fname[10] = L"foo.txt\0";
    UINT64 openmode = EFI_FILE_MODE_READ;
    UINT64 attr = 0;
    status = uefi_call_wrapper(vol_proto->Open, 5, vol_proto, &f, fname, openmode, attr);

    if (EFI_ERROR(status)) {
        Print(L"Error opening file: %r\n", status);
        return EFI_SUCCESS;
    }

    Print(L"opened file %s\n", fname);

    // Spec says can only return EFI_SUCCESS
    status = uefi_call_wrapper(vol_proto->Close, 1, f);

    Print(L"Closed file\n");

    return EFI_SUCCESS;
}

1 ответ

Решение

Где и как получить доступ к конкретному протоколу, статически не определено спецификацией UEFI, но что-то, что необходимо обнаружить во время выполнения. Хотя это немного трудоемко, это позволяет писать приложения / драйверы, переносимые между совершенно разными реализациями UEFI, которые все соответствуют спецификации.

Поэтому вам нужно использовать LocateProtocol() для каждого протокола, который вы собираетесь использовать, прежде чем вы сможете использовать HandleProtocol(). Вам не хватает LOADED_IMAGE_PROTOCOL, потому что он инициализирован вашим ImageHandle со ссылкой на экземпляр исполняемого в данный момент изображения (вашей программы).

Это описано в посте Мэтью под разделом, который описывает, как использовать протоколы, прикрепленные к дескриптору изображения. Как насчет протоколов, которые прикреплены к другим дескрипторам?,

Другие вопросы по тегам