assemblyx86-64nasmuefi

How do I get the device path of the current running image in UEFI?


As part of a UEFI application I am writing, I would like for my application to load a second application in the same folder as this one. However, to do this, I need to know the full file path of the current running file, and none of the firmwares I have access to include a SIMPLE_FILE_SYSTEM_PROTOCOL instance at all (using locateHandleBuffer).

My solution was to get the LOADED_IMAGE_PROTOCOL of my running application, get the device path from there (hoping that it is a file path media device path) and go from there.

The issue is that each run of my application gives a different device path. On some firmware, only the length of the device path changes, but on other firmware, the device path type changes. Also, in all of the firmwares I have tried, none of the device types are valid device types.

How do I get the device path of the file I am trying to load? Is it that this code is broken, or am I going about it wrong, or both?

Here are some screenshots of the messages from the application. The first is QEMU, and the second is from my laptop:

Device path has type 6, subtype (222/223) and info length (random number) Device path has type (random number), subtype (random number) and info length (random number)

Here is the portion of the main program flow that does the device path printing:

;Get the loaded image protocol for this handle
mov rcx, [EFI_HANDLE]
mov rdx, GUID_EFI_LOADED_IMAGE_PROTOCOL
call getProtocolFromHandle

;If getting the protocol failed, exit
cmp rax, [RETURN_SUCCESS]
je print

cmp rax, [RETURN_UNSUPPORTED]
jne error

mov rcx, [CONERR]
mov rdx, protocolNotFound
call printString
jmp printFinalString

print:
;If we have a loaded image protocol, then print information about the device path
add rcx, [OFFSET_LOADED_IMAGE_DEVICE_PATH_PROTOCOL]
mov r8, [rcx]
mov r9, [rcx]
mov r10, [rcx]
and r8, [MASK_FIRST_BYTE]
shr r8, 56
and r9, [MASK_SECOND_BYTE]
shr r9, 48
and r10, [MASK_THIRD_FOURTH_BYTES]
shr r10, 32
mov rcx, [CONOUT]
mov rdx, devicePathOne
call printString
mov rdx, r8
call printNumber
mov rdx, devicePathTwo
call printString
mov rdx, r9
call printNumber
mov rdx, devicePathThree
call printString
mov rdx, r10
call printNumber
mov rdx, newLine
call printString

Here is the portion of the code that gets the protocol from the given handle:

;****************************************************
;*** getProtocolFromHandle [BOOT FUNCTION ONLY]   ***
;*** Definition: Gets the address of the protocol ***
;*** of the specified GUID from the image handle  ***
;*** Input: rcx is the image handle               ***
;***        rdx is the protocol's GUID            ***
;*** Output: rcx is a pointer to the protocol     ***
;***         interface installed on that handle   ***
;*****************************************************
getProtocolFromHandle:
    ;Save registers
    push rdx
    push r8
    push r9
    push r10
    push r11
    
    ;Call the function
    mov r8, ADDRESS_OPEN_PROTOCOL
    mov r9, [EFI_HANDLE]
    mov r10, 0
    mov r11, [CONSTANT_OPEN_PROTOCOL_GET_PROTOCOL]
    push r11 ;Arguments to the stack must be pushed in reverse order
    push r10 
    sub rsp, 0x20
    call [BOOT_SERVICES_OPEN_PROTOCOL]

    ;Restore registers
    add rsp, 0x20
    pop r10
    pop r11
    pop r11
    pop r10
    pop r9
    pop r8
    pop rdx
    
    ;Prepare return values and return
    mov rcx, [ADDRESS_OPEN_PROTOCOL]
    ret

And here is a copy of the data that I think would be useful for understanding the code:

OFFSET_LOADED_IMAGE_DEVICE_PATH_PROTOCOL dq 28
ADDRESS_OPEN_PROTOCOL dq 0
MASK_FIRST_BYTE dq 0xff00000000000000
MASK_SECOND_BYTE dq 0x00ff000000000000
MASK_THIRD_FOURTH_BYTES dq 0x0000ffff00000000

Edit: On one instance of running this application today, it crashed when trying to get a value. This suggests that my address is completely wrong, but I don't know how it is wrong: Device path has type 11, subtype (nothing after that, application crashed)


Solution

  • I have discovered the issue. The main issue here is that the offset for the loaded image device path needs to be 32 instead of 28. Even though the protocol starts with a UINT32 (4 bytes), it seems like it is zero-padded on 64-bit systems to take up 8 bytes.

    Also, the rest of the code above won't work because it has lots of mistakes and needs lots of changes, but at least now you can get the pointer to the device path protocol!

    Something like this will work:

    getImageProtocol:
    ;Get the loaded image protocol for this handle
    mov rcx, [EFI_HANDLE]
    mov rdx, GUID_EFI_LOADED_IMAGE_PROTOCOL
    call getProtocolFromHandle
    cmp rax, [RETURN_SUCCESS]
    jne exitWithError
    mov [ADDRESS_LOADED_IMAGE], rcx
    
    ;Get an instance of the device path utilities protocol
    mov rcx, GUID_EFI_DEVICE_PATH_UTILITIES_PROTOCOL
    call locateProtocol
    cmp rax, [RETURN_SUCCESS]
    jne exitWithError
    mov [ADDRESS_DEVICE_PATH_UTILITIES_PROTOCOL], rcx
    mov rcx, [rcx]
    mov [ADDRESS_DEVICE_PATH_UTILITIES_PROTOCOL_GET_DEVICE_PATH_SIZE], rcx
    
    ;Print the size of the loaded image device path
    mov rcx, [ADDRESS_LOADED_IMAGE]
    add rcx, [OFFSET_LOADED_IMAGE_DEVICE_PATH]
    mov rcx, [rcx]
    call getDevicePathSize
    cmp rax, [RETURN_SUCCESS]
    jne exitWithError
    mov r9, rcx
    mov rcx, [ADDRESS_CONOUT]
    mov rdx, sizeString
    call printString
    mov rdx, r9
    call printNumber
    mov rdx, newLine
    call printString
    

    And the functions look like this (I use tab width 8 so some of the asterisk alignment might look weird in SO since it has tab width 4):

    ;******************************************************************
    ;*** getProtocolFromHandle [BOOT FUNCTION ONLY]     ***
    ;*** Definition: Gets an instance of the protocol which is  ***
    ;***         registered to the given image.     ***
    ;*** Input: rcx is the image handle             ***
    ;***        rdx is a pointer to the protocol's GUID     ***
    ;*** Output: rcx is a pointer to the protocol's interface   ***
    ;***         installed on that handle.              ***
    ;******************************************************************
    getProtocolFromHandle:
        ;Save registers
        push rdx
        push r8
        push r9
        push r10
        push r11
        
        ;Call the function
        mov r8, BUFFER_OPENED_PROTOCOL
        mov r9, [EFI_HANDLE]
        mov r10, 0
        mov r11, [CONSTANT_OPEN_PROTOCOL_GET_PROTOCOL]
        push r11 ;Arguments to the stack must be pushed in reverse order
        push r10 
        sub rsp, 0x20
        call [ADDRESS_BOOT_SERVICES_OPEN_PROTOCOL]
    
        ;Restore registers
        add rsp, 0x20
        pop r10
        pop r11
        pop r11
        pop r10
        pop r9
        pop r8
        pop rdx
        
        ;Prepare return values and return
        mov rcx, [BUFFER_OPENED_PROTOCOL]
        ret
        
    ;**************************************************************************
    ;*** locateProtocol [BOOT FUNCTION ONLY]                ***
    ;*** Definition: provides an interface to the requested protocol.   ***
    ;*** Input: rcx is the address of the protocol GUID         ***
    ;*** Output: rcx holds a pointer to the interface           ***
    ;**************************************************************************
    locateProtocol:
        ;Save registers
        push rdx
        push r8
        push r9
        push r10
        push r11
        
        ;Call the function
        mov rdx, 0
        mov r8, BUFFER_LOCATED_PROTOCOL
        sub rsp, 0x20
        call [ADDRESS_BOOT_SERVICES_LOCATE_PROTOCOL]
    
        ;Restore registers
        add rsp, 0x20
        pop r11
        pop r10
        pop r9
        pop r8
        pop rdx
        
        ;Prepare return values and return
        mov rcx, [BUFFER_LOCATED_PROTOCOL]
        ret
    
    ;**************************************************************************
    ;*** getDevicePathSize                          ***
    ;*** Definition: gets the total size of all nodes in the device ***
    ;***         path, including the end-of-path tag            ***
    ;*** Input: rcx is the address of the device path           ***
    ;*** Output: rcx is the size of the device path         ***
    ;**************************************************************************
    getDevicePathSize:
        ;Save registers
        push rdx
        push r8
        push r9
        push r10
        push r11
        
        ;Call the function
        sub rsp, 0x20
        call [ADDRESS_DEVICE_PATH_UTILITIES_PROTOCOL_GET_DEVICE_PATH_SIZE] ;My gosh that's a long pointer name!
    
        ;Restore registers
        add rsp, 0x20
        pop r11
        pop r10
        pop r9
        pop r8
        pop rdx
        
        ;Return
        mov rcx, rax
        cmp rax, 0
        jne GDPSgood
        mov rax, [RETURN_INVALID_ARGUMENT] ;DevicePath is either null or something else, so it's invalid :(
        ret
        
        GDPSgood:
        mov rax, [RETURN_SUCCESS]
        ret