284 lines
7.0 KiB
C
284 lines
7.0 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
|
|
* Copyright 2012 Intel Corporation; author: H. Peter Anvin
|
|
* Chandramouli Narayanan
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
|
|
* Boston MA 02111-1307, USA; either version 2 of the License, or
|
|
* (at your option) any later version; incorporated herein by reference.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/* Miscellaneous functions for UEFI support
|
|
* We assume that EFI library initialization has completed
|
|
* and we have access to the global EFI exported variables
|
|
*
|
|
*/
|
|
#include "efi.h"
|
|
#include "fio.h"
|
|
|
|
/* Variables that need to be exported
|
|
* efi_errno - maintains the errors from EFI calls to display error messages.
|
|
*/
|
|
EFI_STATUS efi_errno = EFI_SUCCESS;
|
|
|
|
/* Locals
|
|
* vol_root - handle to the root device for file operations
|
|
*/
|
|
static EFI_FILE_HANDLE vol_root;
|
|
|
|
/* Table of UEFI error messages to be indexed with the EFI errno
|
|
* Update error message list as needed
|
|
*/
|
|
static CHAR16 *uefi_errmsg[] = {
|
|
L"EFI_UNDEFINED", /* should not get here */
|
|
L"EFI_LOAD_ERROR",
|
|
L"EFI_INVALID_PARAMETER",
|
|
L"EFI_UNSUPPORTED",
|
|
L"EFI_BAD_BUFFER_SIZE",
|
|
L"EFI_BUFFER_TOO_SMALL",
|
|
L"EFI_NOT_READY",
|
|
L"EFI_DEVICE_ERROR",
|
|
L"EFI_WRITE_PROTECTED",
|
|
L"EFI_OUT_OF_RESOURCES",
|
|
L"EFI_VOLUME_CORRUPTED",
|
|
L"EFI_VOLUME_FULL",
|
|
L"EFI_NO_MEDIA",
|
|
L"EFI_MEDIA_CHANGED",
|
|
L"EFI_NOT_FOUND",
|
|
L"EFI_ACCESS_DENIED",
|
|
L"EFI_NO_RESPONSE",
|
|
L"EFI_NO_MAPPING",
|
|
L"EFI_TIMEOUT",
|
|
L"EFI_NOT_STARTED",
|
|
L"EFI_ALREADY_STARTED",
|
|
L"EFI_ABORTED",
|
|
L"EFI_ICMP_ERROR",
|
|
L"EFI_TFTP_ERROR",
|
|
L"EFI_PROTOCOL_ERROR"
|
|
};
|
|
|
|
static UINTN nerrs = sizeof(uefi_errmsg)/sizeof(CHAR16 *);
|
|
|
|
|
|
/* Generic write error message; there is no gnu lib api to write to StdErr
|
|
* For now, everything goes ConOut
|
|
*/
|
|
void efi_printerr(
|
|
CHAR16 *fmt,
|
|
...
|
|
)
|
|
{
|
|
va_list args;
|
|
va_start (args, fmt);
|
|
VPrint (fmt, args);
|
|
va_end (args);
|
|
}
|
|
|
|
/* Simple console logger of efi-specific error messages. It uses
|
|
* gnu-efi library Print function to do the job.
|
|
*/
|
|
|
|
void efi_perror(CHAR16 *prog)
|
|
{
|
|
/* Ensure that the err number lies within range
|
|
* Beware: unsigned comparisons fail on efi, signed comparisons work
|
|
*/
|
|
if (EFI_ERROR(efi_errno) && (INTN)efi_errno < (INTN)nerrs)
|
|
efi_printerr(L"%s: %s\n", prog, uefi_errmsg[efi_errno]);
|
|
}
|
|
|
|
/* Write to UEFI ConOut */
|
|
void efi_printout(
|
|
CHAR16 *fmt,
|
|
...
|
|
)
|
|
{
|
|
va_list args;
|
|
va_start (args, fmt);
|
|
VPrint (fmt, args);
|
|
va_end (args);
|
|
}
|
|
|
|
/* IMPORTANT:
|
|
* efi_setvol_root() needs to be called from efi main.
|
|
* The rest of the ADV support relies on the file i/o environment
|
|
* setup here. In order to use the EFI file support, we need
|
|
* to set up the volume root. Subsequent file operations need the root to
|
|
* access the interface routines.
|
|
*
|
|
*/
|
|
|
|
EFI_STATUS efi_set_volroot(EFI_HANDLE device_handle)
|
|
{
|
|
vol_root = LibOpenRoot(device_handle);
|
|
if (!vol_root) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/* File operations using EFI runtime services */
|
|
|
|
/* Open the file using EFI runtime service
|
|
* Opening a file in EFI requires a handle to the device
|
|
* root in order to use the interface to the file operations supported by UEFI.
|
|
* For now, assume device volume root handle from the loaded image
|
|
*
|
|
* Return a valid handle if open succeeded and null otherwise.
|
|
* UEFI returns a bogus handle on error, so return null handle on error.
|
|
*
|
|
* TODO:
|
|
* 1. Validate the assumption about the root device
|
|
* 2. Can EFI open a file with full path name specification?
|
|
* 3. Look into gnu-efi helper functions for dealing with device path/file path
|
|
* 4. Consider utilizing EFI file open attributes.
|
|
* 5. In EFI, file attributes can be specified only at the time of creation.
|
|
* How do we support the equivalent of set_attributes() and clear_attributes()
|
|
*/
|
|
EFI_FILE_HANDLE efi_open(CHAR16 *file, UINT64 mode)
|
|
{
|
|
/* initialize with NULL handle since EFI open returns bogus */
|
|
EFI_FILE_HANDLE fd = NULL;
|
|
|
|
ASSERT(vol_root);
|
|
|
|
/* Note that the attributes parameter is none for now */
|
|
efi_errno = uefi_call_wrapper(vol_root->Open,
|
|
5,
|
|
vol_root,
|
|
&fd,
|
|
file,
|
|
mode,
|
|
0);
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
* read/write wrapper functions for UEFI
|
|
*
|
|
* Read or write the specified number of bytes starting at the
|
|
* offset specified.
|
|
*
|
|
* Returns:
|
|
* number of bytes read/written on success
|
|
* -1 on error
|
|
*/
|
|
/* Wrapper function to read from a file */
|
|
size_t efi_xpread(EFI_FILE_HANDLE fd, void *buf, size_t count, off_t offset)
|
|
{
|
|
ASSERT(fd);
|
|
efi_errno = uefi_call_wrapper(fd->SetPosition,
|
|
2,
|
|
fd,
|
|
offset);
|
|
if (EFI_ERROR(efi_errno)) return -1;
|
|
efi_errno = uefi_call_wrapper(fd->Read,
|
|
3,
|
|
fd,
|
|
&count,
|
|
buf);
|
|
if (EFI_ERROR(efi_errno)) return -1;
|
|
return count;
|
|
}
|
|
|
|
/* Wrapper function to write */
|
|
size_t efi_xpwrite(EFI_FILE_HANDLE fd, void *buf, size_t count, off_t offset)
|
|
{
|
|
ASSERT(fd);
|
|
efi_errno = uefi_call_wrapper(fd->SetPosition,
|
|
2,
|
|
fd,
|
|
offset);
|
|
if (EFI_ERROR(efi_errno)) return -1;
|
|
efi_errno = uefi_call_wrapper(fd->Write,
|
|
3,
|
|
fd,
|
|
&count,
|
|
buf);
|
|
if (EFI_ERROR(efi_errno)) return -1;
|
|
return count;
|
|
}
|
|
|
|
/* For an open handle, return the generic file info excluding
|
|
* the variable-length filename in the EFI_FILE_INFO structure.
|
|
*/
|
|
int efi_fstat(EFI_FILE_HANDLE fd, EFI_FILE_INFO *st)
|
|
{
|
|
EFI_FILE_INFO *finfo;
|
|
|
|
ASSERT(fd);
|
|
finfo = LibFileInfo(fd);
|
|
if (finfo) {
|
|
uefi_call_wrapper(BS->CopyMem, 3, (VOID *)st, (VOID *)finfo, SIZE_OF_EFI_FILE_INFO);
|
|
FreePool(finfo);
|
|
return 0;
|
|
}
|
|
/* gnu-efi lib does not return EFI status; export a generic device error for now */
|
|
efi_errno = EFI_DEVICE_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
/* set/clear_attributes()
|
|
* Currently handles only VFAT filesystem
|
|
* TODO:
|
|
* 1. Assumes VFAT file system.
|
|
* 2. How do we support other file systems?
|
|
*/
|
|
void efi_set_attributes(EFI_FILE_HANDLE fd)
|
|
{
|
|
EFI_FILE_INFO *finfo;
|
|
|
|
ASSERT(fd);
|
|
finfo = LibFileInfo(fd);
|
|
if (finfo) {
|
|
/* Hidden+System+Readonly */
|
|
finfo->Attribute = EFI_FILE_READ_ONLY|EFI_FILE_HIDDEN|EFI_FILE_SYSTEM;
|
|
efi_errno = uefi_call_wrapper(fd->SetInfo,
|
|
4,
|
|
fd,
|
|
&GenericFileInfo,
|
|
finfo->Size,
|
|
finfo);
|
|
FreePool(finfo);
|
|
} else efi_errno = EFI_NOT_FOUND;
|
|
}
|
|
|
|
void efi_clear_attributes(EFI_FILE_HANDLE fd)
|
|
{
|
|
EFI_FILE_INFO *finfo;
|
|
|
|
ASSERT(fd);
|
|
finfo = LibFileInfo(fd);
|
|
if (finfo) {
|
|
finfo->Attribute = 0; /* no attributes */
|
|
efi_errno = uefi_call_wrapper(fd->SetInfo,
|
|
4,
|
|
fd,
|
|
&GenericFileInfo,
|
|
finfo->Size,
|
|
finfo);
|
|
FreePool(finfo);
|
|
} else efi_errno = EFI_NOT_FOUND;
|
|
}
|
|
|
|
/* Implement the sync operation using the EFI Flush file operation*/
|
|
void efi_sync(EFI_FILE_HANDLE fd)
|
|
{
|
|
ASSERT(fd);
|
|
efi_errno = uefi_call_wrapper(fd->Flush, 1, fd);
|
|
return;
|
|
}
|
|
|
|
/* Close the file */
|
|
void efi_close(EFI_FILE_HANDLE fd)
|
|
{
|
|
|
|
ASSERT(fd);
|
|
efi_errno = uefi_call_wrapper(fd->Close, 1, fd);
|
|
return;
|
|
}
|