Есть ли руководство по переносу edk2 на новую платформу ARM64?

#arm64 #edk2

Вопрос:

Я новичок в EDK2.

Для переноса прошивки ekd2 на новую платформу ARM64 было бы неплохо сначала получить минимальный порт edk2, который может запускать, по крайней мере, оболочку UEFI, улучшения могут добавляться постепенно на основе этого.

Кажется, что первый шаг довольно крутой, например, как определить минимальный набор «элементов» в .dsc .fdf файле и для платформы? В моем случае я хотел бы создать .fd для своей платформы и рассматривать ее как BL33 из TF-A, фактически я хотел бы создать прошивку edk2 для замены u-boot.

Кажется, что такое руководство трудно найти в Интернете. Я нашел старую версию edk2, которая содержит некоторые инструкции, но, по-видимому, они устарели (не существуют в последней master ветке, хотя их можно найти в ветках UDK, таких как UDK2014 ), и я не уверен, почему эти документы удалены из master ветки.

В настоящее время я могу создавать .fd для FVP ( edk2-platforms/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc ), и кажется, что выходные данные сборки FVP_AARCH64_EFI.fd должны обрабатываться как BL33. Теоретически это может быть прототипом для моей новой платформы ARM64, но для меня это слишком сложно для начала: размер прошивки составляет около 2,5 Мбайт (по сравнению с 500 КБ u-boot), поэтому я думаю, что это далеко не «минимальная» версия. но трудно понять, какие функции следует удалить (и как).

Мне интересно, есть ли подробное руководство по такой теме…

Ответ №1:

После 1 месяца проб и ошибок сегодня мне удалось перенести мою платформу ARM64 в среду оболочки UEFI. Я рассматриваю это как свою первую веху на пути к EDK2. Ниже я попытаюсь обобщить шаги, которые я предпринял до сих пор, в качестве предварительного ответа на мой вопрос выше. Руководство / исправления / комментарии приветствуются.

  1. Ознакомьтесь со спецификациями UEFI / PI и реализацией EDK2, прочитав книги / спецификации / статьи. Ну, спецификации UEFI / PI занимают тысячи страниц…с чего начать? Мой основной список чтения:
    • «Помимо Bios — разработка с использованием унифицированного расширяемого интерфейса прошивки», 3-е издание, Винсент Циммер и др. Как объяснили авторы, книга представляет собой своего рода высокоуровневое резюме спецификаций на тысячи страниц. И я нахожу, что книга хорошо организована для новичка, чтобы ознакомиться с различными концепциями, связанными с UEFI. Основная цель 1-го чтения (перед началом работы с кодовой базой edk2) — ознакомиться с концепциями и архитектурными идеями, а не с деталями. С соответствующими разделами необходимо ознакомиться позже при чтении реализаций EDK2.
    • Спецификации EDK2, в том числе:
      • Руководство пользователя EDKII
      • Спецификация сборки EDKII
      • Спецификация файла EDKII DSC / FDF / DEC / INF
    • Различные статьи в Интернете…
  2. Получите эталонную платформу, которая может корректно загружать образ FD, созданный из последнего исходного кода EDK2, и немного поиграйте с менеджером загрузки и оболочкой. В моем случае я выбрал RPi4B. Для меня это очень важно, поскольку эталонная платформа служит поручнем в течение всего процесса, что всякий раз, когда я сталкиваюсь с ошибками или у меня возникают сомнения, я проверяю источник / журнал эталонной платформы. Это решает большинство проблем, с которыми я столкнулся. Кстати, всегда генерируйте «журнал сборки» и «отчет о сборке» как для справочной платформы, так и для целевой платформы, поскольку два файла содержат очень подробную информацию для сравнения и проверки. Обратитесь к спецификации сборки EDK2 о том, как сгенерировать эти два файла во время сборки.

    Я использую следующий скрипт для сборки для платформы RPi4B:

     #!/bin/bash
    
    # https://github.com/tianocore/edk2-platforms#how-to-build-linux-environment
    
    export WORKSPACE=/home/bruin/work/tianocore
    export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms:$WORKSPACE/edk2-non-osi
    
    pushd $WORKSPACE
    
    rm -rf ./Build/RPi4
    
    source edk2/edksetup.sh
    
    echo "Building BaseTools..."
    make -C edk2/BaseTools all
    
    #sudo apt install acpica-tools    # iasl
    # pip install antlr4-python3-runtime   # -Y EXECUTION_ORDER
    
    echo "Building firmware for Pi4B..."
    GCC5_AARCH64_PREFIX=aarch64-none-linux-gnu- build 
        -n 4 
        -a AARCH64 
        -p Platform/RaspberryPi/RPi4/RPi4.dsc 
        -t GCC5 
        -b NOOPT 
        -v -d 9 -j RPi4-build.log 
        -y RPi4-build-report.txt 
        -Y PCD 
        -Y LIBRARY 
        -Y DEPEX 
        -Y HASH 
        -Y BUILD_FLAGS 
        -Y FLASH 
        -Y FIXED_ADDRESS 
        -Y EXECUTION_ORDER 
        all
     

    How to use the build result RPI_EFI.fd on RPi4B, consult the following:

    • edk2-platforms/Platform/RaspberryPi/RPi4/Readme.md
    • readme.md inside https://github.com/pftf/RPi4/releases/download/v1.17/RPi4_UEFI_Firmware_v1.32.zip. btw, I need to replace the original start4.elf and fixup4.dat with the ones in the zip file, otherwise, the boot of RPi4 will fail, complaining something like below:
       RpiFirmwareGetClockRate: Get Clock Rate return: ClockRate=0 ClockId=C
      ASSERT [ArasanMMCHost] /home/bruin/work/tianocore/edk2-platforms/Platform/RaspberryPi/
      Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c(263): BaseFrequency != 0
       
    • Стоит проанализировать RPI_EFI.fd содержимое в некоторой степени, используя некоторые утилиты UEFI. Я в основном использую версию GUI UEFITool sudo apt install uefitool uefitool-cli . Также доступны другие инструменты. Анотомия RPI_EFI.fd помогает при чтении спецификаций сборки EDK2 для проверки понимания концепций.
      • Одним из особых аспектов RPI_EFI.fd является то, что 1-й 128K является bl31.bin двоичным из ATF. Я предполагаю, что это связано со специальными методами настройки подключения при загрузке для RPi. Для моей платформы мне не нужна такая упаковка, мне нужно только создать образ UEFI MY.fd , который обрабатывается как образ BL33 и упаковывается fip.bin вместе с образами BL2 и BL31 с помощью скрипта сборки ATF.
      • Еще один аспект, на который следует обратить внимание, — это «вектор сброса» в начале .fd файла. Это связано с точкой входа образа UEFI (и точкой входа каждого модуля EDK2), а также интерпретацией BL инструкции для AArch64. В принципе, это можно резюмировать следующим образом:

      Первый [Components] из RPI_EFI.fd ArmPlatformPkg/PrePi/PeiUniCore.inf них — это of MODULE_TYPE = SEC .

      • Что это за компонент: это первый (и единственный) SEC модуль (безопасности) в RPi4. Что PrePi Pei означает название и?

        … спецификация PI не привязана к edk2 PEIMS, и я не вижу, где модули EDKII PEI в настоящее время являются единственной «признанной» кремниевой средой инициализации. Само дерево edk2, похоже, содержит платформы, которые вообще не используют набор модулей edk2 PEI, но (IIRC) переходят от SEC к DXE. Я считаю, что «ArmPlatformPkg / PrePi» и «ArmVirtPkg / PrePi» связаны с этим.

        https://listman.redhat.com/archives/edk2-devel-archive/2020-November/msg00021.html

      • Его точка входа: все компоненты UEFI имеют одинаковую точку входа ( _ModuleEntryPoint ).
        • Под «компонентом» подразумевается либо драйвер UEFI, либо приложение UEFI, оба являются исполняемыми файлами PE32, обычно с суффиксом .efi .
        • Файлы .efi преобразуются из исполняемых файлов ELF ( .dll ) с помощью GenFw инструмента: изменение заголовков файлов.
        • Чтобы убедиться, что «точка входа всех компонентов» _ModuleEntryPoint :
          • Проверьте .dll командную строку генерации в build report ( build -y <BUILD_REPORT_FILE> ), у нас есть два флага "aarch64-none-linux-gnu-gcc" -o xxx.dll -u _ModuleEntryPoint -Wl,-e,_ModuleEntryPoint ... :
            • -u : gcc --help -v|grep "undefined SYMBOL" дает -u SYMBOL --undefined SYMBOL: star with undefined reference to SYMBOL .
            • Wl,-e : ld --help|grep "entry" дает -e ADDRESS, --entry ADDRESS Set start address .
          • Проверьте все .dll файлы, которые Entry point address == _ModuleEntryPoint : find . -type f -name "*.dll" -exec sh -c "readelf -a {} |grep -E 'Entry point address|_ModuleEntryPoint'" ;
      • Его точка входа — это точка входа всего образа UEFI FD (т. Е. От bl33_base_addr перехода к этому _ModuleEntryPoint ):

        Топология файла прошивки UEFI

        Файл прошивки UEFI (на самом деле файл прошивки UEFI — FD Device — FD) представляет собой набор двоичных файлов UEFI, инкапсулированных в один образ. Формат этого изображения определяется спецификацией инициализации платформы, том 3. В основе этого файла находится векторная таблица. Инструкция перехода ‘BL’ в основании firwmare (местоположение записи сброса в векторной таблице) перейдет к первому модулю ‘SEC’ образа прошивки UEFI.

        https://github.com/lzeng14/tianocore/wiki/ArmPkg-Debugging

        • Для проверки приведенных выше утверждений:
          • Разберите вектор сброса (т. Е. 1-е слово) сгенерированного .FD (мы получили смещение = 0x360 ):
             $ xxd -l 4 -e TEST.fd       <== dump 4 bytes in little endian
            00000000: 140000d8          <== BL   {PC} (0xd8<<2); offset=0x360
             
          • Проверьте точку входа в .dll (мы получили смещение = 0x240 ):
             $ aarch64-none-elf-objdump -t ArmPlatformPrePiUniCore.dll|grep _ModuleEntryPoint
            0000000000000240 g     F .text  0000000000000000 _ModuleEntryPoint
            $ readelf -h ArmPlatformPrePiUniCore.dll|grep Entry
            Entry point address:               0x240
             
          • Сравните содержимое двух файлов с разным смещением (мы получили идентичный контент):
             $ xxd -s 0x360 -l 64 TEST.fd                      <== skip 0x360 bytes, dump 64 bytes
            00000360: 901e 0094 050a 0094 ea03 00aa a1cd 0a58  ...............X
            00000370: 0200 e0d2 2200 c0f2 0240 a0f2 0200 80f2  ...."....@......
            00000380: c303 a0d2 e3ff 9ff2 6304 00d1 6300 028b  ........c...c...
            00000390: 0400 a1d2 0400 80f2 2000 03eb 8400 0054  ........ ......T
            $ xxd -s 0x240 -l 64 ArmPlatformPrePiUniCore.dll  <== skip 0x240 bytes
            00000240: 901e 0094 050a 0094 ea03 00aa a1cd 0a58  ...............X
            00000250: 0200 e0d2 2200 c0f2 0240 a0f2 0200 80f2  ...."....@......
            00000260: c303 a0d2 e3ff 9ff2 6304 00d1 6300 028b  ........c...c...
            00000270: 0400 a1d2 0400 80f2 2000 03eb 8400 0054  ........ ......T
             
  3. Подготовьте пустой pkg и сделайте так, чтобы он был собран нормально. Основная цель — выполнить некоторые упражнения с системой сборки EDK2 и использовать пустой pkg в качестве отправной точки для новой платформы.
    • Сделайте копию RaspberryPi.dec , измените все gRaspberry на gMyPlatform .
    • Сделайте копию RPi4.dsc и RPi4.fdf и закомментируйте все содержимое в DSC FDF файле and.
    • Замените все идентификаторы GUID в DSC / FDF / DEC files, сгенерировав новые с помощью онлайн-генератора идентификаторов guid.
    • Обратите внимание, что PCD объявляются в DEC файлах, а файлы DEC обозначаются модулями ( INF файлами). Поскольку пустой пакет не содержит модуля, в нем не будет доступно определение PCD FDF . Итак, для успешной сборки пустого пакета нам нужно закомментировать все ссылки PCD FDF .
    • Команда NOOPT сборки для MyPlatform как показано ниже:
       #!/bin/bash
      
      export WORKSPACE=/home/bruin/work/tianocore
      export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms:$WORKSPACE/edk2-non-osi
      pushd $WORKSPACE
      source edk2/edksetup.sh
      
      echo "Building BaseTools..."
      make -C edk2/BaseTools all
      
      echo "Building UEFI firmware for MyPlatform..."
      GCC5_AARCH64_PREFIX=aarch64-none-linux-gnu- build 
            -n 4 
            -a AARCH64 
            -p Platform/MyCorp/MyPlatform/MyPlatform.dsc 
            -t GCC5 
            -b NOOPT 
            -v -d 9 -j MyPlatform-build.log 
            -y MyPlatform-build-report.txt 
            -Y EXECUTION_ORDER 
            -Y PCD 
            -Y LIBRARY 
            -Y DEPEX 
            -Y HASH 
            -Y BUILD_FLAGS 
            -Y FLASH 
            -Y FIXED_ADDRESS 
            all
      popd
       
  4. Add the 1st component ArmPlatformPrePiUniCore . This component is to prepare the HOBs for DXE phae. The main purpose is to get serial port working and memory config correct. Another purpose of this step is to familiar with steps for adding a component/module/lib. Below is a brief summary of the steps:
    • Uncomment the module’s INF into both DSC ( [Components] section), and FDF ( [FV.FVMAIN_COMPACT] ).
    • Перестройте pkg и устраните все Instance of library class [xxxLib] is not found сообщенные ошибки, обновив [LibraryClasses] разделы DSC .
      • This step is a repeating process for dozens of times.
      • Some lib-class has multiple lib-instances, making sure choose the appropriate lib-instance (ref the build-report of RPi4).
      • при возникновении ModuleEntryPoint.iiii:31: Error: immediate out of range : включить gArmTokenSpaceGuid.PcdFdBaseAddress и gArmTokenSpaceGuid.PcdFdSize FDF включить.
      • если встречается undefined reference to _gPcd_BinaryPatch_PcdSerialClockRate : установить PcdSerialClockRate в [PcdsPatchableInModule] разделе DSC в. FIXME: почему? ссылка.
    • Проверьте PCD, перечисленные в журнале сборки: проверьте все ненормальные значения PCD и укажите правильные значения.
    • Настройка драйверов или библиотек, зависящих от платформы.
      • SerialPortLib : найдите файл заголовка lib-класса ( MdePkg/Include/Library/SerialPortLib.h ) с помощью find edk2 -type f -name "*.dec" -exec grep -Hn SerialPortLib . Требуются следующие функции:
        • SerialPortInitialize()
        • SerialPortWrite()
        • SerialPortRead()
        • SerialPortPoll()
        • SerialPortSetControl() : RETURN_UNSUPPORTED
        • SerialPortGetControl() : RETURN_UNSUPPORTED
        • SerialPortSetAttributes() : RETURN_UNSUPPORTED
      • ArmPlatformLib : заголовок интерфейса at Include/Library/ArmPlatformLib.h . Требуются следующие функции:
        • ArmPlatformGetCorePosition() : верните идентификатор процессора в кластере с учетом значения MPIDR. эта функция используется _ModuleEntryPoint для настройки стека для вторичных ядер. На данный момент предполагается один кластер.
        • ArmPlatformIsPrimaryCore()
        • ArmPlatformGetPrimaryCoreMpId()
        • ArmPlatformGetBootMode()
        • ArmPlatformPeiBootAction()
        • ArmPlatformInitialize()
        • ArmPlatformGetVirtualMemoryMap()
        • ArmPlatformGetPlatformPpiList()
      • и т.д…
  5. Раскомментируйте больше модулей в DSC / FDF, модуль за модулем…Для драйверов / библиотек, зависящих от платформы RPi, мы можем:
    • либо выполните поиск в edk2 / edk2-platform для похожих экземпляров драйверов или библиотек, либо
    • скопируйте реализацию RPi4 и закомментируйте большую часть содержимого, сначала выполните успешную сборку pkg, а затем исправьте ошибку.
  6. Отладка: мой текущий основной метод отладки заключается в добавлении «printf ()», то есть макроса DEBUG((DEBUG_INFO,)) edk2 . gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel Для просмотра дополнительной информации об отладке необходимо установить соответствующее значение.