Loading files under UEFI

From OSDev Wiki
Jump to navigation Jump to search

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