You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用loadImage返回EFI_NOT_FOUND的原因与解决方法

UEFI应用加载二级镜像时EFI_NOT_FOUND问题排查

问题描述

使用NASM汇编编写UEFI应用,尝试通过绝对文件路径加载二级EFI文件并调用loadImage启动服务运行镜像,但遇到以下问题:

  • 设备路径变量非空,但多UEFI系统测试均返回EFI_NOT_FOUND错误
  • 将设备路径设为null时返回无效参数,但UEFI规范仅在设备路径与源缓冲区均为null时才会返回EFI_NOT_FOUND(已确认设备路径非空)

相关代码

初始loadImage实现

;******************************************************************
;*** loadImage [BOOT FUNCTION ONLY]                             ***
;*** Definition: Load an EFI boot driver from a file            ***
;*** Input: rcx is a pointer to a device path protocol to load  ***
;*** Output: rcx is the EFI handle                              ***
;******************************************************************
loadImage:
    ;Save registers
    push rdx
    push r8
    push r9
    push r10
    push r11
    
    ;Call the function
    mov r8, rcx
    mov rcx, 0
    mov rdx, [EFI_HANDLE]
    mov r8, turtleDevicePathProtocol    
    mov r9, 0
    mov r10, 0
    mov r11, BUFFER_LOAD_IMAGE
    push r11
    push r10
    
    sub rsp, 0x20
    call [ADDRESS_BOOT_SERVICES_LOAD_IMAGE]
    
    ;Restore registers
    add rsp, 0x30   
    pop r11
    pop r10
    pop r9
    pop r8
    pop rdx
    
    ;Return
    mov rcx, [BUFFER_LOAD_IMAGE]
    ret

初始设备路径定义

;****************************************
;*** Bootloader device path protocols ***
;****************************************
turtleDevicePathProtocol    db 4 ;This is the image I am trying to load
                db 4
                dw 36
                db __utf16__ `\\EFI\\turtle.efi\0`
                db 0x7f
                db 0xff
                dw 4                
bootloaderDevicePathProtocol    db 4 ;This is the loaded image protocol, for reference
                db 4
                dw 48
                db __utf16__ `\\EFI\\BOOT\\BOOTX64.efi\0`
                db 0x7f
                db 0xff
                dw 4

编辑后的路径拼接及调用代码

; We need to append two device paths together to load the second image
; First, we need the device path linked to the parent EFI handle
; Second, we need the absolute file path of the image to load
; Third, we append them together
; Finally, we load and start the image at the appended device path

; So, step 1:
mov rcx, [ADDRESS_LOADED_IMAGE_DEVICE_HANDLE]
mov rdx, GUID_EFI_DEVICE_PATH_PROTOCOL
call getProtocolFromHandle
cmp rax, [RETURN_SUCCESS]
jne exitWithError
mov [ADDRESS_DEVICE_PATH], rcx

; Step 2
mov rdx, turtleDevicePathProtocol

; Step 3
call appendDevicePath
cmp rax, [RETURN_SUCCESS]
jne exitWithError

loadDevicePath:
mov [ADDRESS_DEVICE_PATH], rcx

; Print the device path if we can. Note that this function uses the optional
; DEVICE_PATH_TO_TEXT_PROTOCOL, so we will just skip this step if we can't find it

mov rcx, GUID_EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
call locateProtocol
cmp rax, [RETURN_SUCCESS]
je printImagePath
cmp rax, [RETURN_NOT_FOUND]
jne exitWithError
call warnWithError
jmp noPrintDevicePath

printImagePath:
; Get the print device path function
mov [ADDRESS_DEVICE_PATH_TO_TEXT_PROTOCOL], rcx
;mov rcx, [ADDRESS_DEVICE_PATH_TO_TEXT_PROTOCOL] ;Obviously line is not needed right?
add rcx, [OFFSET_DEVICE_PATH_TO_TEXT_CONVERT_PATH]
mov rcx, [rcx]
mov [ADDRESS_DEVICE_PATH_TO_TEXT_PROTOCOL_CONVERT_PATH_TO_TEXT], rcx

;Print the device path
mov rcx, [ADDRESS_DEVICE_PATH]
mov rdx, [ADDRESS_CONOUT]
call printDevicePath
mov rcx, rdx
mov rdx, newLine
call printString

noPrintDevicePath:
;Load turtle image
mov rcx, [ADDRESS_DEVICE_PATH]
call loadImage
cmp rax, [RETURN_SUCCESS]
jne exitWithError

输出信息

Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/\EFI\turtle.efi
Instruction failed with exit code 14. Turtle is stopping!

问题分析及解决方向

1. loadImage函数参数传递错误

初始loadImage实现中存在明显逻辑错误:函数接收的设备路径参数(rcx)先被存入r8,但随后又被硬编码为turtleDevicePathProtocol覆盖,导致传入UEFILoadImage服务的始终是硬编码的错误路径,而非拼接后的正确路径。

修正方案:
保留传入的设备路径参数,不要覆盖r8的值:

;Call the function
mov rcx, 0                          ; ImageHandle = NULL(加载应用时传NULL)
mov rdx, [EFI_HANDLE]               ; ParentHandle = 当前应用的Handle
mov r8, rcx                         ; 使用函数输入的设备路径参数
mov r9, 0                           ; SourceBuffer = NULL
mov r10, 0                          ; SourceSize = 0
mov r11, BUFFER_LOAD_IMAGE          ; Destination = 输出加载后的Handle的地址
push r11
push r10

2. 设备路径节点长度计算错误

turtleDevicePathProtocol中的文件路径节点长度dw 36不符合实际:

  • UTF-16字符串\\EFI\\turtle.efi\0包含13个字符(含终止null),每个字符占2字节,总字节数为26
  • 加上设备路径节点头部的4字节(Type=4, SubType=4, Length=2字节),整个节点的总长度应为dw 30,而非36

修正方案:
调整turtleDevicePathProtocol的长度字段:

turtleDevicePathProtocol    db 4
                            db 4
                            dw 30 ; 修正为正确长度
                            db __utf16__ `\\EFI\\turtle.efi\0`
                            db 0x7f
                            db 0xff
                            dw 4

3. 路径拼接后的内存处理问题

UEFI的AppendDevicePath服务会分配新内存存储拼接后的设备路径,需确认:

  • appendDevicePath函数是否正确调用UEFI标准服务,返回的拼接路径指针有效
  • 使用完拼接路径后,调用FreePool释放内存,避免内存泄漏

4. 存储设备访问问题

从输出路径Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/\EFI\turtle.efi来看,当前应用从Firmware Volume(FV)加载,但turtle.efi可能不在同一FV,或FV为只读无法访问文件系统路径:

  • 确认turtle.efi所在存储设备已被正确枚举,且挂载了文件系统协议
  • 尝试基于当前应用LoadedImage协议的文件路径构建相对路径,而非绝对路径

验证步骤

  1. 修正loadImage参数传递错误,确保传入拼接后的正确路径
  2. 调整设备路径节点的长度值,保证格式合规
  3. 用DevicePathToText输出完整路径,确认节点顺序、格式无错误
  4. 检查AppendDevicePath返回值,确认内存分配成功
  5. 手动调用文件系统协议打开turtle.efi,验证文件可访问性

内容的提问来源于stack exchange,提问作者JD9999

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 19:14:54