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:
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:
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