Skip to the content.

Linux UEFI 学习环境搭建

运行第一个 UEFI 程序

编译 efi

参考教程 https://www.rodsbooks.com/efi-programming/hello.html 但是这个教程有点老,参考 stackoverflow 可以修复。

使用 vn/code/module/gnuefi/ 来构建。

运行 efi

参考 osdev 上,我构建出来了一个小脚本

uefi.sh,其参数为将要测试的 efi. 然后在 QEMU 的图形界面中,可以看到 UEFI shell, 在其中输入 FS0:,最后执行程序。

交叉编译

类似,如果想要在 x86 电脑上编译安装 ARM 版本的 edk2,其 Conf/target.txt 对应的配置为:

ACTIVE_PLATFORM       = ArmVirtPkg/ArmVirtQemu.dsc
TARGET_ARCH           = AARCH64
TOOL_CHAIN_TAG        = GCC5
MAX_CONCURRENT_THREAD_NUMBER = 50

参考这篇 blog

运行 build 之前,首先执行:

export GCC5_AARCH64_PREFIX=aarch64-linux-gnu-          # ubuntu 中
export GCC5_AARCH64_PREFIX=aarch64-unknown-linux-gnu-  # nixos 中

构建基于 StdLib 的 HelloWorld

上面是调用原生的 uefi 接口来构建的程序,实际上,UEFI 提供了 StdLib,其尽可能提供和 glibc 相同的接口,这样,很多用户态程序几乎不需要做任何修改就可以 直接编译为 .efi 文件,在 UEFI shell 中运行了。

使用方法很简单:

# 下载
git clone https://github.com/tianocore/edk2-libc
# 将 edk2-libc 的内容拷贝到 edk2 中
mv edk2-libc/* path/to/edk2
cd path/to/edk2
# 编译
build -p AppPkg/AppPkg.dsc

其实 edk2-libc 主要就是两个文件夹:

  1. StdLib : 利用 UEFI native 的接口实现 glib 的接口
  2. AppPkg : 各种测试程序,甚至包括 lua 解释器

在 Linux 上调试 edk2

使用 gdb 调试

https://github.com/tianocore/tianocore.github.io/wiki/How-to-debug-OVMF-with-QEMU-using-GDB 这里需要手动计算偏移,不是 debuginfo 的格式有问题,而是 debuginfo 不知道 qemu 会把 efi 加载到哪里,。 所以

这里介绍了如何自动加载 debuginfo https://retrage.github.io/2019/12/05/debugging-ovmf-en.html

其实就是 edk2 生成的符号信息进行一些转换之后才可以被 gdb 识别,

  1. 准备环节,只需要操作一次
    # 生成 /tmp/ovmf.log 启动包含各个 module 加载的地址信息
    ./uefi.sh
    # 根据 /tmp/ovmf.log 和 Build 下 .debug 生成 gdb 可识别调试信息
    ./uefi.sh -g
    
  2. 调试:
    # 在第一次窗口,启动 QEMU
    ./uefi.sh -s
    # 在第二个窗口,启动 gdb
    ./uefi.sh -d
    

最后效果:

需要注意的事情是,打断点需要使用 hardware breakpoint

使用 debugcon 调试

在源码中添加调试语句,然后重新编译运行

  DEBUG((DEBUG_INFO, "%s\n", "hello"));

内核作为 efi 文件启动

内核实际上可以作为 efi 文件在 UEFI 上执行, 具体参考内核文档

让程序运行 shell 命令

参考1

#include <Library/ShellLib.h>
#include <Library/UefiLib.h>
#include <Uefi.h>

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

  ShellExecute(&ImageHandle, L"echo Hello World!", FALSE, NULL, &Status);

  return Status;
}
## @file
#  A simple, basic, EDK II native, "hello" application.
#
#   Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
#   SPDX-License-Identifier: BSD-2-Clause-Patent
#
##

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = Hello
  FILE_GUID                      = a912f198-7f0e-4803-b908-b757b806ec83
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = UefiMain

#
#  VALID_ARCHITECTURES           = IA32 X64
#

[Sources]
  Hello.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  UefiLib
  ShellCEntryLib
  ShellLib

使用 Rust 编写 UEFI Application

我们使用 uefi-rs 来感受一下。 进入到 template 目录中,按照 https://github.com/rust-osdev/uefi-rs/blob/master/BUILDING.md 操作即可。

相对 edk2 而言,uefi-rs 的代码量非常少,如果你恰好是 Rust 高手,读读其代码还是相当有意思的。

资源

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。

  1. https://stackoverflow.com/questions/38738862/run-a-uefi-shell-command-from-inside-uefi-application