Skip to content

Commit

Permalink
work around Linux kernel efistub limitation with GetInfo()
Browse files Browse the repository at this point in the history
* The Linux kernel's EFI stub uses a fixed size GetInfo() buffer and has no provision
  whatsoever for retrying with a larger buffer, as it should, on getting a valid
  EFI_BUFFER_TOO_SMALL response.
* Because the default buffer size we request is larger than what the kernel expects,
  this leads to the kernel bailing out on EFI_BUFFER_TOO_SMALL.
* We therefore reduce the size of the default EFI_FILE_INFO buffer we use to the size
  that the kernel expects, and use this opportunity to replace the sizeof(EFI_FILE_INFO)
  constants with the more appropriate SIZE_OF_EFI_FILE_INFO.
* Closes #26.
* Closes #27.
* Closes #38.
  • Loading branch information
pbatard committed Sep 4, 2022
1 parent 7eeec94 commit d31ec7b
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 28 deletions.
8 changes: 4 additions & 4 deletions src/driver.h
Expand Up @@ -103,7 +103,7 @@

/* Driver version */
#define FS_DRIVER_VERSION_MAJOR 1
#define FS_DRIVER_VERSION_MINOR 7
#define FS_DRIVER_VERSION_MINOR 9

#ifndef ARRAYSIZE
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
Expand All @@ -119,9 +119,9 @@
#define _WIDEN(s) L ## s
#define WIDEN(s) _WIDEN(s)

#define MAX_PATH 256
#define MINIMUM_INFO_LENGTH (sizeof(EFI_FILE_INFO) + MAX_PATH * sizeof(CHAR16))
#define MINIMUM_FS_INFO_LENGTH (sizeof(EFI_FILE_SYSTEM_INFO) + MAX_PATH * sizeof(CHAR16))
#define MAX_FILE_NAME_LEN 256
#define MINIMUM_INFO_LENGTH (SIZE_OF_EFI_FILE_INFO + MAX_FILE_NAME_LEN * sizeof(CHAR16))
#define MINIMUM_FS_INFO_LENGTH (SIZE_OF_EFI_FILE_SYSTEM_INFO + MAX_FILE_NAME_LEN * sizeof(CHAR16))
#define IS_ROOT(File) (File == File->FileSystem->RootFile)

/* Logging */
Expand Down
34 changes: 16 additions & 18 deletions src/file.c
Expand Up @@ -35,7 +35,7 @@ static const CHAR16 *
FileName(EFI_GRUB_FILE *File)
{
EFI_STATUS Status;
static CHAR16 Path[MAX_PATH];
static CHAR16 Path[MAX_FILE_NAME_LEN];

Status = Utf8ToUtf16NoAlloc(File->path, Path, sizeof(Path));
if (EFI_ERROR(Status)) {
Expand Down Expand Up @@ -82,7 +82,7 @@ FileOpen(EFI_FILE_HANDLE This, EFI_FILE_HANDLE *New,
EFI_GRUB_FILE *NewFile;

// TODO: Use dynamic buffers?
char path[MAX_PATH], clean_path[MAX_PATH], *dirname;
char path[MAX_FILE_NAME_LEN], clean_path[MAX_FILE_NAME_LEN], *dirname;
INTN i, len;
BOOLEAN AbsolutePath = (*Name == L'\\');

Expand Down Expand Up @@ -136,7 +136,7 @@ FileOpen(EFI_FILE_HANDLE This, EFI_FILE_HANDLE *New,
/* We only want to handle absolute paths */
clean_path[0] = '/';
/* Find out if we're dealing with root by removing the junk */
CopyPathRelative(&clean_path[1], path, MAX_PATH - 1);
CopyPathRelative(&clean_path[1], path, MAX_FILE_NAME_LEN - 1);
if (clean_path[1] == 0) {
/* We're dealing with the root */
PrintInfo(L" Reopening <ROOT>\n");
Expand Down Expand Up @@ -275,6 +275,7 @@ DirHook(const CHAR8 *name, const GRUB_DIRHOOK_INFO *DirInfo, VOID *Data)
EFI_STATUS Status;
EFI_FILE_INFO *Info = (EFI_FILE_INFO *) Data;
INT64 *Index = (INT64 *) &Info->FileSize;
UINTN tmpLen;
CHAR8 *filename = (CHAR8 *) (UINTN) Info->PhysicalSize;
EFI_TIME Time = { 1970, 01, 01, 00, 00, 00, 0, 0, 0, 0, 0};

Expand All @@ -288,14 +289,14 @@ DirHook(const CHAR8 *name, const GRUB_DIRHOOK_INFO *DirInfo, VOID *Data)

strcpya(filename, name);

Status = Utf8ToUtf16NoAlloc(filename, Info->FileName, (INTN)(Info->Size - sizeof(EFI_FILE_INFO)));
tmpLen = (UINTN)(Info->Size - SIZE_OF_EFI_FILE_INFO);
Status = Utf8ToUtf16NoAllocUpdateLen(filename, Info->FileName, &tmpLen);
Info->Size = SIZE_OF_EFI_FILE_INFO + tmpLen;
if (EFI_ERROR(Status)) {
if (Status != EFI_BUFFER_TOO_SMALL)
PrintStatusError(Status, L"Could not convert directory entry to UTF-8");
return (INT32) Status;
}
/* The Info struct size already accounts for the extra NUL */
Info->Size = sizeof(*Info) + StrLen(Info->FileName) * sizeof(CHAR16);

// Oh, and of course GRUB uses a 32 bit signed mtime value (seriously, wtf guys?!?)
if (DirInfo->MtimeSet)
Expand Down Expand Up @@ -328,7 +329,7 @@ FileReadDir(EFI_GRUB_FILE *File, UINTN *Len, VOID *Data)
INT64 *Index = (INT64 *) &Info->FileSize;
/* And PhysicalSize as a pointer to our filename */
CHAR8 **basename = (CHAR8 **) &Info->PhysicalSize;
CHAR8 path[MAX_PATH];
CHAR8 path[MAX_FILE_NAME_LEN];
EFI_GRUB_FILE *TmpFile = NULL;
INTN len;

Expand Down Expand Up @@ -552,7 +553,7 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data)
return EFI_BUFFER_TOO_SMALL;
}

ZeroMem(Data, sizeof(EFI_FILE_INFO));
ZeroMem(Data, SIZE_OF_EFI_FILE_INFO);
Info->Size = *Len;
Info->Attribute = EFI_FILE_READ_ONLY;
GrubTimeToEfiTime(File->Mtime, &Time);
Expand All @@ -567,16 +568,15 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data)
Info->PhysicalSize = GrubGetFileSize(File);
}

/* The Info struct size accounts for the NUL string terminator */
tmpLen = (UINTN)(Info->Size - sizeof(EFI_FILE_INFO));
tmpLen = (UINTN)(Info->Size - SIZE_OF_EFI_FILE_INFO);
Status = Utf8ToUtf16NoAllocUpdateLen(File->basename, Info->FileName, &tmpLen);
if (EFI_ERROR(Status)) {
if (Status != EFI_BUFFER_TOO_SMALL)
PrintStatusError(Status, L"Could not convert basename to UTF-16");
return Status;
}

Info->Size = sizeof(EFI_FILE_INFO) + tmpLen;
Info->Size = SIZE_OF_EFI_FILE_INFO + tmpLen;
*Len = (INTN)Info->Size;
return EFI_SUCCESS;

Expand All @@ -589,7 +589,7 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data)
return EFI_BUFFER_TOO_SMALL;
}

ZeroMem(Data, sizeof(EFI_FILE_INFO));
ZeroMem(Data, SIZE_OF_EFI_FILE_SYSTEM_INFO);
FSInfo->Size = *Len;
FSInfo->ReadOnly = 1;
/* NB: This should really be cluster size, but we don't have access to that */
Expand All @@ -616,17 +616,16 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data)
if (EFI_ERROR(Status)) {
PrintStatusError(Status, L"Could not read disk label");
FSInfo->VolumeLabel[0] = 0;
*Len = sizeof(EFI_FILE_SYSTEM_INFO);
*Len = SIZE_OF_EFI_FILE_SYSTEM_INFO + sizeof(CHAR16);
} else {
/* The Info struct size accounts for the NUL string terminator */
tmpLen = (INTN)(FSInfo->Size - sizeof(EFI_FILE_SYSTEM_INFO));
tmpLen = (INTN)(FSInfo->Size - SIZE_OF_EFI_FILE_SYSTEM_INFO);
Status = Utf8ToUtf16NoAllocUpdateLen(label, FSInfo->VolumeLabel, &tmpLen);
if (EFI_ERROR(Status)) {
if (Status != EFI_BUFFER_TOO_SMALL)
PrintStatusError(Status, L"Could not convert label to UTF-16");
return Status;
}
FSInfo->Size = sizeof(EFI_FILE_SYSTEM_INFO) + tmpLen;
FSInfo->Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + tmpLen;
*Len = (INTN)FSInfo->Size;
}
return EFI_SUCCESS;
Expand All @@ -637,8 +636,7 @@ FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data)
Status = GrubLabel(File, &label);
if (EFI_ERROR(Status)) {
PrintStatusError(Status, L"Could not read disk label");
}
else {
} else {
Status = Utf8ToUtf16NoAllocUpdateLen(label, VLInfo->VolumeLabel, Len);
if (EFI_ERROR(Status)) {
if (Status != EFI_BUFFER_TOO_SMALL)
Expand Down
13 changes: 7 additions & 6 deletions src/utf8.c
Expand Up @@ -631,7 +631,7 @@ CHAR16
}

/**
* Convert an UTF-8 string to UTF-16, using an user supplied buffer
* Convert an UTF-8 string to UTF-16, using a user supplied buffer
*
* @v Src A NUL terminated UTF-8 input string
* @v Dst A pointer an UTF-16 string buffer
Expand All @@ -658,11 +658,12 @@ Utf8ToUtf16NoAllocUpdateLen(CHAR8 *Src, CHAR16 *Dst, UINTN *Len)

SrcLen++; /* +1 for NUL terminator */

if (!ConvertUcs2Utf8(TRUE, (UINT8 *)Src, SrcLen, (UINT8 *)Dst, *Len, Len))
return EFI_NO_MAPPING;

if (*Len > OrgLen)
return EFI_BUFFER_TOO_SMALL;
if (!ConvertUcs2Utf8(TRUE, (UINT8*)Src, SrcLen, (UINT8*)Dst, *Len, Len)) {
if (*Len > OrgLen)
return EFI_BUFFER_TOO_SMALL;
else
return EFI_NO_MAPPING;
}

return EFI_SUCCESS;
}
Expand Down

0 comments on commit d31ec7b

Please sign in to comment.