UEFI编程中LocateProtocol返回efi_not_found的问题排查与解决
问题原因及解决方案
可能的原因
- 启动阶段错误:在
ExitBootServices之后调用LocateProtocol,此时Boot Services已关闭,无法访问文件系统协议;或者启动介质未被UEFI识别(比如ESP分区未格式化、未挂载)。 - GUID定义错误:Pascal代码中
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL的GUID与UEFI规范不匹配,导致协议无法被定位。 - 参数传递错误:
LocateProtocol的输出参数类型不匹配,或者传递了无效的指针。 - 构建环境问题:未使用针对UEFI的编译目标,或者链接了错误的库,导致二进制与UEFI环境不兼容。
Pascal修复方案
1. 修正GUID定义
UEFI规范中EFI_SIMPLE_FILE_SYSTEM_PROTOCOL的GUID为{0x0964E5B2, 0x6459, 0x11D2, {0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B}},确保Pascal中的定义完全一致。
2. 确认调用时机
必须在Boot Services阶段(即EFI_IMAGE_ENTRY_POINT执行期间)调用LocateProtocol,避免在ExitBootServices之后操作。
3. 修复后的Pascal代码示例
program UEFIFileSystemTest; {$mode objfpc}{$H+} {$TUEFI} uses uefi; var Status: EFI_STATUS; SimpleFileSystem: PEFI_SIMPLE_FILE_SYSTEM_PROTOCOL; RootDir: PEFI_FILE_PROTOCOL; begin // 确保Boot Services可用 if gBS = nil then Exit; // 正确的Simple File System Protocol GUID const EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID: EFI_GUID = ( Data1: $0964E5B2; Data2: $6459; Data3: $11D2; Data4: ($8E, $39, $00, $A0, $C9, $69, $72, $3B) ); // 定位协议 Status := gBS^.LocateProtocol(@EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, nil, @SimpleFileSystem); if EFI_ERROR(Status) then begin gST^.ConOut^.OutputString(gST^.ConOut, 'Failed to locate Simple File System Protocol: '); gST^.ConOut^.OutputString(gST^.ConOut, UEFIStatusToString(Status)); gST^.ConOut^.OutputString(gST^.ConOut, #10#13); Exit; end; // 打开根目录 Status := SimpleFileSystem^.OpenVolume(SimpleFileSystem, @RootDir); if EFI_ERROR(Status) then begin gST^.ConOut^.OutputString(gST^.ConOut, 'Failed to open root volume'#10#13); Exit; end; gST^.ConOut^.OutputString(gST^.ConOut, 'Successfully accessed file system'#10#13); // 释放资源 RootDir^.Close(RootDir); end.
4. 修正构建脚本(Free Pascal)
#!/bin/bash # 编译为64位UEFI应用(若为32位则替换-Px86_64为-Pi386) fpc -Tuefi -Px86_64 UEFIFileSystemTest.pas # 将生成的.efi文件复制到ESP分区的EFI/BOOT目录
C语言实现示例
#include <efi.h> #include <efilib.h> EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { EFI_STATUS Status; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem; EFI_FILE_PROTOCOL *RootDir; InitializeLib(ImageHandle, SystemTable); // 定位文件系统协议 Status = uefi_call_wrapper(BS->LocateProtocol, 3, &gEfiSimpleFileSystemProtocolGuid, NULL, (void**)&SimpleFileSystem); if (EFI_ERROR(Status)) { Print(L"Failed to locate Simple File System Protocol: %r\n", Status); return Status; } // 打开根目录 Status = uefi_call_wrapper(SimpleFileSystem->OpenVolume, 2, SimpleFileSystem, &RootDir); if (EFI_ERROR(Status)) { Print(L"Failed to open root volume: %r\n", Status); return Status; } Print(L"Successfully accessed file system\n"); // 关闭根目录 uefi_call_wrapper(RootDir->Close, 1, RootDir); return EFI_SUCCESS; }
构建脚本(GCC)
#!/bin/bash # 编译64位UEFI应用 x86_64-w64-mingw32-gcc -ffreestanding -fshort-wchar -mno-red-zone -m64 -c main.c -o main.o x86_64-w64-mingw32-ld -nostdlib -Wl,-entry:efi_main -Wl,--subsystem,10 -o main.efi main.o -lefi -lgnuefi
Rust语言实现示例(基于uefi-rs库)
use uefi::{prelude::*, proto::media::file::File, proto::media::fs::SimpleFileSystem}; #[entry] fn main(_image_handle: Handle, system_table: SystemTable<Boot>) -> Status { let stdout = system_table.stdout(); let boot_services = system_table.boot_services(); // 定位文件系统协议 let fs = boot_services.locate_protocol::<SimpleFileSystem>() .map_err(|e| { let _ = stdout.write_str(format!("Failed to locate Simple File System Protocol: {:?}\n", e).as_str()); e.status() })?; // 打开根目录 let mut root_dir = fs.open_volume() .map_err(|e| { let _ = stdout.write_str(format!("Failed to open root volume: {:?}\n", e).as_str()); e.status() })?; let _ = stdout.write_str("Successfully accessed file system\n"); // 关闭文件(uefi-rs会自动处理资源,此处可省略) let _ = root_dir.close(); Status::SUCCESS }
构建脚本(Cargo)
#!/bin/bash # 确保已安装uefi-rs依赖,编译为64位UEFI应用 cargo build --target x86_64-unknown-uefi --release # 生成的efi文件路径:target/x86_64-unknown-uefi/release/<项目名>.efi
内容的提问来源于stack exchange,提问作者user24150775
相关产品推荐
相关产品推荐

