Loading files under UEFI: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (added GNU-EFI)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(3 intermediate revisions by 3 users not shown)
Line 1: Line 1:
UEFI supposed to provide an easy way of loading files from partitions. Sadly it is not so easy, considerably more complicated than [[Reading_sectors_under_UEFI|reading sectors]], but at least it parses the file system for you.
UEFI is supposed to provide an easy way of loading files from partitions. Sadly it is not so easy, considerably more complicated than [[Reading_sectors_under_UEFI|reading sectors]], but at least it parses the file system for you.

HINT: [[POSIX-UEFI]] makes this a whole lot easier by providing an "fopen" / "fread" / "fclose" interface for you under UEFI.


== Volume Handle ==
== Volume Handle ==


There's no common "Open File" function in UEFI. Instead each volume has its own. So the first thing you need to do is locate a volume handle. You probably want to use the same file system that your application was loaded from, so you need to use the image handle provided to your '''efi_main'''. Then you need to use the EFI_LOADED_IMAGE_PROTOCOL to figure out the device your application resides on.
There's no common "Open File" function in UEFI. Instead each volume has its own. So the first thing you need to do is locate a volume handle. You probably want to use the same file system that your application was loaded from, so you need to use the image handle provided to your '''efi_main'''. Then you need to use the EFI_LOADED_IMAGE_PROTOCOL to figure out the device your application resides on.
<source lang="c">
<syntaxhighlight lang="c">
EFI_FILE_HANDLE GetVolume(EFI_HANDLE image)
EFI_FILE_HANDLE GetVolume(EFI_HANDLE image)
{
{
Line 33: Line 31:
}
}


</syntaxhighlight>
</source>
Here [[GNU-EFI]]'s libefi.a provides a '''LibOpenRoot''' function, which can be used instead of IOVolume:
Here [[GNU-EFI]]'s libefi.a provides a '''LibOpenRoot''' function, which can be used instead of IOVolume:
<source lang="c">
<syntaxhighlight lang="c">
/* get the volume handle */
/* get the volume handle */
return LibOpenRoot(loaded_image->DeviceHandle);
return LibOpenRoot(loaded_image->DeviceHandle);
</syntaxhighlight>
</source>


== Read Data from File ==
== Read Data from File ==
Line 45: Line 43:


=== Open ===
=== Open ===
<source lang="c">
<syntaxhighlight lang="c">
CHAR16 *FileName = L"DATA.TXT";
CHAR16 *FileName = L"DATA.TXT";
EFI_FILE_HANDLE FileHandle;
EFI_FILE_HANDLE FileHandle;
Line 51: Line 49:
/* open the file */
/* open the file */
uefi_call_wrapper(Volume->Open, 5, Volume, &FileHandle, FileName, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
uefi_call_wrapper(Volume->Open, 5, Volume, &FileHandle, FileName, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
</syntaxhighlight>
</source>
Note that the file name passed to '''Volume->Open''' is a UNICODE16 string, meaning all characters are 2 bytes long. To get the L"" string literal correctly, you'll need to pass the "-fshort-wchar" command line option to gcc.
Note that the file name passed to '''Volume->Open''' is a UNICODE16 string, meaning all characters are 2 bytes long. To get the L"" string literal correctly, you'll need to pass the "-fshort-wchar" command line option to gcc.


=== Read ===
=== Read ===
<source lang="c">
<syntaxhighlight lang="c">
/* read from the file */
/* read from the file */
UINT64 ReadSize = FileSize(FileHandle);
UINT64 ReadSize = FileSize(FileHandle);
Line 61: Line 59:


uefi_call_wrapper(FileHandle->Read, 3, FileHandle, &ReadSize, Buffer);
uefi_call_wrapper(FileHandle->Read, 3, FileHandle, &ReadSize, Buffer);
</syntaxhighlight>
</source>


=== Close ===
=== Close ===
<source lang="c">
<syntaxhighlight lang="c">
/* close the file */
/* close the file */
uefi_call_wrapper(FileHandle->Close, 1, FileHandle);
uefi_call_wrapper(FileHandle->Close, 1, FileHandle);
</syntaxhighlight>
</source>


=== Get File Size ===
=== Get File Size ===


In order to know how much to read ('''ReadSize''' variable above), you'll need to know the file's size. For that, you should use '''FileHandle->GetInfo'''. The problem is, there's no way of knowing how big buffer the information structure requires, so you'll have to grow the buffer dynamically if GetInfo fails. GNU-EFI provides a pretty handy wrapper for that, [https://sourceforge.net/p/gnu-efi/code/ci/master/tree/lib/hand.c LibFileInfo].
In order to know how much to read ('''ReadSize''' variable above), you'll need to know the file's size. For that, you should use '''FileHandle->GetInfo'''. The problem is, there's no way of knowing how big buffer the information structure requires, so you'll have to grow the buffer dynamically if GetInfo fails. GNU-EFI provides a pretty handy wrapper for that, [https://sourceforge.net/p/gnu-efi/code/ci/master/tree/lib/hand.c LibFileInfo].
<source lang="c">
<syntaxhighlight lang="c">
UINT64 FileSize(EFI_FILE_HANDLE FileHandle)
UINT64 FileSize(EFI_FILE_HANDLE FileHandle)
{
{
Line 83: Line 81:
return ret;
return ret;
}
}
</syntaxhighlight>
</source>


== See also ==
== See also ==
Line 98: Line 96:
* [https://sourceforge.net/p/gnu-efi/code/ci/master/tree/ GNU-EFI source]
* [https://sourceforge.net/p/gnu-efi/code/ci/master/tree/ GNU-EFI source]


[[Category:x86-64]]
[[Category:UEFI]]
[[Category:UEFI]]

Latest revision as of 04:34, 9 June 2024

UEFI is supposed to provide an easy way of loading files from partitions. Sadly it is not so easy, considerably more complicated than reading sectors, but at least it parses the file system for you.

Volume Handle

There's no common "Open File" function in UEFI. Instead each volume has its own. So the first thing you need to do is locate a volume handle. You probably want to use the same file system that your application was loaded from, so you need to use the image handle provided to your efi_main. Then you need to use the EFI_LOADED_IMAGE_PROTOCOL to figure out the device your application resides on.

EFI_FILE_HANDLE GetVolume(EFI_HANDLE image)
{
  EFI_LOADED_IMAGE *loaded_image = NULL;                  /* image interface */
  EFI_GUID lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;      /* image interface GUID */
  EFI_FILE_IO_INTERFACE *IOVolume;                        /* file system interface */
  EFI_GUID fsGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; /* file system interface GUID */
  EFI_FILE_HANDLE Volume;                                 /* the volume's interface */

  /* get the loaded image protocol interface for our "image" */
  uefi_call_wrapper(BS->HandleProtocol, 3, image, &lipGuid, (void **) &loaded_image);
  /* get the volume handle */
  uefi_call_wrapper(BS->HandleProtocol, 3, loaded_image->DeviceHandle, &fsGuid, (VOID*)&IOVolume);
  uefi_call_wrapper(IOVolume->OpenVolume, 2, IOVolume, &Volume);
  return Volume;
}

/**
 * This is your application's main entry point, which receives its image handle
 */
int efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
{
  EFI_FILE_HANDLE Volume = GetVolume(image);

  /* ... */
}

Here GNU-EFI's libefi.a provides a LibOpenRoot function, which can be used instead of IOVolume:

  /* get the volume handle */
  return LibOpenRoot(loaded_image->DeviceHandle);

Read Data from File

Now that we have a Volume instance, it's rather easy to use it. It has the classic Open / Read / Close abstraction.

Open

  CHAR16              *FileName = L"DATA.TXT";
  EFI_FILE_HANDLE     FileHandle;

  /* open the file */
  uefi_call_wrapper(Volume->Open, 5, Volume, &FileHandle, FileName, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);

Note that the file name passed to Volume->Open is a UNICODE16 string, meaning all characters are 2 bytes long. To get the L"" string literal correctly, you'll need to pass the "-fshort-wchar" command line option to gcc.

Read

  /* read from the file */
  UINT64              ReadSize = FileSize(FileHandle);
  UINT8               *Buffer = AllocatePool(ReadSize);

  uefi_call_wrapper(FileHandle->Read, 3, FileHandle, &ReadSize, Buffer);

Close

  /* close the file */
  uefi_call_wrapper(FileHandle->Close, 1, FileHandle);

Get File Size

In order to know how much to read (ReadSize variable above), you'll need to know the file's size. For that, you should use FileHandle->GetInfo. The problem is, there's no way of knowing how big buffer the information structure requires, so you'll have to grow the buffer dynamically if GetInfo fails. GNU-EFI provides a pretty handy wrapper for that, LibFileInfo.

UINT64 FileSize(EFI_FILE_HANDLE FileHandle)
{
  UINT64 ret;
  EFI_FILE_INFO       *FileInfo;         /* file information structure */
  /* get the file's size */
  FileInfo = LibFileInfo(FileHandle);
  ret = FileInfo->FileSize;
  FreePool(FileInfo);
  return ret;
}

See also

Articles

External Links