From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c | 1203 ++++++++++++++++++++ 1 file changed, 1203 insertions(+) create mode 100644 roms/edk2/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c (limited to 'roms/edk2/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c') diff --git a/roms/edk2/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c b/roms/edk2/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c new file mode 100644 index 000000000..a66bcb136 --- /dev/null +++ b/roms/edk2/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c @@ -0,0 +1,1203 @@ +/** @file + Support a Semi Host file system over a debuggers JTAG + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Portions copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "SemihostFs.h" + +#define DEFAULT_SEMIHOST_FS_LABEL L"SemihostFs" + +STATIC CHAR16 *mSemihostFsLabel; + +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gSemihostFs = { + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, + VolumeOpen +}; + +EFI_FILE gSemihostFsFile = { + EFI_FILE_PROTOCOL_REVISION, + FileOpen, + FileClose, + FileDelete, + FileRead, + FileWrite, + FileGetPosition, + FileSetPosition, + FileGetInfo, + FileSetInfo, + FileFlush +}; + +// +// Device path for semi-hosting. It contains our autogened Caller ID GUID. +// +typedef struct { + VENDOR_DEVICE_PATH Guid; + EFI_DEVICE_PATH_PROTOCOL End; +} SEMIHOST_DEVICE_PATH; + +SEMIHOST_DEVICE_PATH gDevicePath = { + { + { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0 } }, + EFI_CALLER_ID_GUID + }, + { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } } +}; + +typedef struct { + LIST_ENTRY Link; + UINT64 Signature; + EFI_FILE File; + CHAR8 *FileName; + UINT64 OpenMode; + UINT32 Position; + UINTN SemihostHandle; + BOOLEAN IsRoot; + EFI_FILE_INFO Info; +} SEMIHOST_FCB; + +#define SEMIHOST_FCB_SIGNATURE SIGNATURE_32( 'S', 'H', 'F', 'C' ) +#define SEMIHOST_FCB_FROM_THIS(a) CR(a, SEMIHOST_FCB, File, SEMIHOST_FCB_SIGNATURE) +#define SEMIHOST_FCB_FROM_LINK(a) CR(a, SEMIHOST_FCB, Link, SEMIHOST_FCB_SIGNATURE); + +EFI_HANDLE gInstallHandle = NULL; +LIST_ENTRY gFileList = INITIALIZE_LIST_HEAD_VARIABLE (gFileList); + +SEMIHOST_FCB * +AllocateFCB ( + VOID + ) +{ + SEMIHOST_FCB *Fcb = AllocateZeroPool (sizeof (SEMIHOST_FCB)); + + if (Fcb != NULL) { + CopyMem (&Fcb->File, &gSemihostFsFile, sizeof (gSemihostFsFile)); + Fcb->Signature = SEMIHOST_FCB_SIGNATURE; + } + + return Fcb; +} + +VOID +FreeFCB ( + IN SEMIHOST_FCB *Fcb + ) +{ + // Remove Fcb from gFileList. + RemoveEntryList (&Fcb->Link); + + // To help debugging... + Fcb->Signature = 0; + + FreePool (Fcb); +} + + + +EFI_STATUS +VolumeOpen ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE **Root + ) +{ + SEMIHOST_FCB *RootFcb = NULL; + + if (Root == NULL) { + return EFI_INVALID_PARAMETER; + } + + RootFcb = AllocateFCB (); + if (RootFcb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RootFcb->IsRoot = TRUE; + RootFcb->Info.Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; + + InsertTailList (&gFileList, &RootFcb->Link); + + *Root = &RootFcb->File; + + return EFI_SUCCESS; +} + +/** + Open a file on the host system by means of the semihosting interface. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to source location. + @param[out] NewHandle A pointer to the location to return the opened + handle for the new file. + @param[in] FileName The Null-terminated string of the name of the file + to be opened. + @param[in] OpenMode The mode to open the file : Read or Read/Write or + Read/Write/Create + @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these + are the attribute bits for the newly created file. The + mnemonics of the attribute bits are : EFI_FILE_READ_ONLY, + EFI_FILE_HIDDEN, EFI_FILE_SYSTEM, EFI_FILE_RESERVED, + EFI_FILE_DIRECTORY and EFI_FILE_ARCHIVE. + + @retval EFI_SUCCESS The file was open. + @retval EFI_NOT_FOUND The specified file could not be found. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_WRITE_PROTECTED Attempt to create a directory. This is not possible + with the semi-hosting interface. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ +EFI_STATUS +FileOpen ( + IN EFI_FILE *This, + OUT EFI_FILE **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + SEMIHOST_FCB *FileFcb; + RETURN_STATUS Return; + EFI_STATUS Status; + UINTN SemihostHandle; + CHAR8 *AsciiFileName; + UINT32 SemihostMode; + UINTN Length; + + if ((FileName == NULL) || (NewHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ( (OpenMode != EFI_FILE_MODE_READ) && + (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) && + (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE)) ) { + return EFI_INVALID_PARAMETER; + } + + if ((OpenMode & EFI_FILE_MODE_CREATE) && + (Attributes & EFI_FILE_DIRECTORY) ) { + return EFI_WRITE_PROTECTED; + } + + Length = StrLen (FileName) + 1; + AsciiFileName = AllocatePool (Length); + if (AsciiFileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeStrToAsciiStrS (FileName, AsciiFileName, Length); + + // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory + if ((AsciiStrCmp (AsciiFileName, "\\") == 0) || + (AsciiStrCmp (AsciiFileName, "/") == 0) || + (AsciiStrCmp (AsciiFileName, "") == 0) || + (AsciiStrCmp (AsciiFileName, ".") == 0) ) { + FreePool (AsciiFileName); + return (VolumeOpen (&gSemihostFs, NewHandle)); + } + + // + // No control is done here concerning the file path. It is passed + // as it is to the host operating system through the semi-hosting + // interface. We first try to open the file in the read or update + // mode even if the file creation has been asked for. That way, if + // the file already exists, it is not truncated to zero length. In + // write mode (bit SEMIHOST_FILE_MODE_WRITE up), if the file already + // exists, it is reset to an empty file. + // + if (OpenMode == EFI_FILE_MODE_READ) { + SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY; + } else { + SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE; + } + Return = SemihostFileOpen (AsciiFileName, SemihostMode, &SemihostHandle); + + if (RETURN_ERROR (Return)) { + if (OpenMode & EFI_FILE_MODE_CREATE) { + // + // In the create if does not exist case, if the opening in update + // mode failed, create it and open it in update mode. The update + // mode allows for both read and write from and to the file. + // + Return = SemihostFileOpen ( + AsciiFileName, + SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE, + &SemihostHandle + ); + if (RETURN_ERROR (Return)) { + Status = EFI_DEVICE_ERROR; + goto Error; + } + } else { + Status = EFI_NOT_FOUND; + goto Error; + } + } + + // Allocate a control block and fill it + FileFcb = AllocateFCB (); + if (FileFcb == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + FileFcb->FileName = AsciiFileName; + FileFcb->SemihostHandle = SemihostHandle; + FileFcb->Position = 0; + FileFcb->IsRoot = 0; + FileFcb->OpenMode = OpenMode; + + Return = SemihostFileLength (SemihostHandle, &Length); + if (RETURN_ERROR (Return)) { + Status = EFI_DEVICE_ERROR; + FreeFCB (FileFcb); + goto Error; + } + + FileFcb->Info.FileSize = Length; + FileFcb->Info.PhysicalSize = Length; + FileFcb->Info.Attribute = (OpenMode & EFI_FILE_MODE_CREATE) ? Attributes : 0; + + InsertTailList (&gFileList, &FileFcb->Link); + + *NewHandle = &FileFcb->File; + + return EFI_SUCCESS; + +Error: + + FreePool (AsciiFileName); + + return Status; +} + +/** + Worker function that truncate a file specified by its name to a given size. + + @param[in] FileName The Null-terminated string of the name of the file to be opened. + @param[in] Size The target size for the file. + + @retval EFI_SUCCESS The file was truncated. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + +**/ +STATIC +EFI_STATUS +TruncateFile ( + IN CHAR8 *FileName, + IN UINTN Size + ) +{ + EFI_STATUS Status; + RETURN_STATUS Return; + UINTN FileHandle; + UINT8 *Buffer; + UINTN Remaining; + UINTN Read; + UINTN ToRead; + + Status = EFI_DEVICE_ERROR; + FileHandle = 0; + Buffer = NULL; + + Return = SemihostFileOpen ( + FileName, + SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY, + &FileHandle + ); + if (RETURN_ERROR (Return)) { + goto Error; + } + + Buffer = AllocatePool (Size); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Read = 0; + Remaining = Size; + while (Remaining > 0) { + ToRead = Remaining; + Return = SemihostFileRead (FileHandle, &ToRead, Buffer + Read); + if (RETURN_ERROR (Return)) { + goto Error; + } + Remaining -= ToRead; + Read += ToRead; + } + + Return = SemihostFileClose (FileHandle); + FileHandle = 0; + if (RETURN_ERROR (Return)) { + goto Error; + } + + Return = SemihostFileOpen ( + FileName, + SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY, + &FileHandle + ); + if (RETURN_ERROR (Return)) { + goto Error; + } + + if (Size > 0) { + Return = SemihostFileWrite (FileHandle, &Size, Buffer); + if (RETURN_ERROR (Return)) { + goto Error; + } + } + + Status = EFI_SUCCESS; + +Error: + + if (FileHandle != 0) { + SemihostFileClose (FileHandle); + } + if (Buffer != NULL) { + FreePool (Buffer); + } + + return (Status); + +} + +/** + Close a specified file handle. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to close. + + @retval EFI_SUCCESS The file was closed. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. + +**/ +EFI_STATUS +FileClose ( + IN EFI_FILE *This + ) +{ + SEMIHOST_FCB *Fcb; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS(This); + + if (!Fcb->IsRoot) { + SemihostFileClose (Fcb->SemihostHandle); + // + // The file size might have been reduced from its actual + // size on the host file system with FileSetInfo(). In + // that case, the file has to be truncated. + // + if (Fcb->Info.FileSize < Fcb->Info.PhysicalSize) { + TruncateFile (Fcb->FileName, Fcb->Info.FileSize); + } + FreePool (Fcb->FileName); + } + + FreeFCB (Fcb); + + return EFI_SUCCESS; +} + +/** + Close and delete a file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to delete. + + @retval EFI_SUCCESS The file was closed and deleted. + @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. + +**/ +EFI_STATUS +FileDelete ( + IN EFI_FILE *This + ) +{ + SEMIHOST_FCB *Fcb; + RETURN_STATUS Return; + CHAR8 *FileName; + UINTN NameSize; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (!Fcb->IsRoot) { + // Get the filename from the Fcb + NameSize = AsciiStrLen (Fcb->FileName); + FileName = AllocatePool (NameSize + 1); + + AsciiStrCpyS (FileName, NameSize + 1, Fcb->FileName); + + // Close the file if it's open. Disregard return status, + // since it might give an error if the file isn't open. + This->Close (This); + + // Call the semihost interface to delete the file. + Return = SemihostFileRemove (FileName); + if (RETURN_ERROR (Return)) { + return EFI_WARN_DELETE_FAILURE; + } + return EFI_SUCCESS; + } else { + return EFI_WARN_DELETE_FAILURE; + } +} + +/** + Read data from an open file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to read data from. + @param[in out] BufferSize On input, the size of the Buffer. On output, the + amount of data returned in Buffer. In both cases, + the size is measured in bytes. + @param[out] Buffer The buffer into which the data is read. + + @retval EFI_SUCCESS The data was read. + @retval EFI_DEVICE_ERROR On entry, the current file position is + beyond the end of the file, or the semi-hosting + interface reported an error while performing the + read operation. + @retval EFI_INVALID_PARAMETER At least one of the three input pointers is NULL. + +**/ +EFI_STATUS +FileRead ( + IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + SEMIHOST_FCB *Fcb; + EFI_STATUS Status; + RETURN_STATUS Return; + + if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (Fcb->IsRoot) { + // The semi-hosting interface does not allow to list files on the host machine. + Status = EFI_UNSUPPORTED; + } else { + Status = EFI_SUCCESS; + if (Fcb->Position >= Fcb->Info.FileSize) { + *BufferSize = 0; + if (Fcb->Position > Fcb->Info.FileSize) { + Status = EFI_DEVICE_ERROR; + } + } else { + Return = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer); + if (RETURN_ERROR (Return)) { + Status = EFI_DEVICE_ERROR; + } else { + Fcb->Position += *BufferSize; + } + } + } + + return Status; +} + +/** + Worker function that extends the size of an open file. + + The extension is filled with zeros. + + @param[in] Fcb Internal description of the opened file + @param[in] Size The number of bytes, the file has to be extended. + + @retval EFI_SUCCESS The file was extended. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + +**/ +STATIC +EFI_STATUS +ExtendFile ( + IN SEMIHOST_FCB *Fcb, + IN UINTN Size + ) +{ + RETURN_STATUS Return; + UINTN Remaining; + CHAR8 WriteBuffer[128]; + UINTN WriteNb; + UINTN WriteSize; + + Return = SemihostFileSeek (Fcb->SemihostHandle, Fcb->Info.FileSize); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + + Remaining = Size; + SetMem (WriteBuffer, 0, sizeof(WriteBuffer)); + while (Remaining > 0) { + WriteNb = MIN (Remaining, sizeof(WriteBuffer)); + WriteSize = WriteNb; + Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, WriteBuffer); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + Remaining -= WriteNb; + } + + return EFI_SUCCESS; +} + +/** + Write data to an open file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to write data to. + @param[in out] BufferSize On input, the size of the Buffer. On output, the + size of the data actually written. In both cases, + the size is measured in bytes. + @param[in] Buffer The buffer of data to write. + + @retval EFI_SUCCESS The data was written. + @retval EFI_ACCESS_DENIED Attempt to write into a read only file or + in a file opened in read only mode. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_INVALID_PARAMETER At least one of the three input pointers is NULL. + +**/ +EFI_STATUS +FileWrite ( + IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + SEMIHOST_FCB *Fcb; + EFI_STATUS Status; + UINTN WriteSize; + RETURN_STATUS Return; + UINTN Length; + + if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + // We cannot write a read-only file + if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY) + || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) { + return EFI_ACCESS_DENIED; + } + + // + // If the position has been set past the end of the file, first grow the + // file from its current size "Fcb->Info.FileSize" to "Fcb->Position" + // size, filling the gap with zeros. + // + if (Fcb->Position > Fcb->Info.FileSize) { + Status = ExtendFile (Fcb, Fcb->Position - Fcb->Info.FileSize); + if (EFI_ERROR (Status)) { + return Status; + } + Fcb->Info.FileSize = Fcb->Position; + } + + WriteSize = *BufferSize; + Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + + Fcb->Position += *BufferSize; + if (Fcb->Position > Fcb->Info.FileSize) { + Fcb->Info.FileSize = Fcb->Position; + } + + Return = SemihostFileLength (Fcb->SemihostHandle, &Length); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + Fcb->Info.PhysicalSize = Length; + + return EFI_SUCCESS; +} + +/** + Return a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to get the current position on. + @param[out] Position The address to return the file's current position value. + + @retval EFI_SUCCESS The position was returned. + @retval EFI_INVALID_PARAMETER The parameter "This" or "Position" is NULL. + +**/ +EFI_STATUS +FileGetPosition ( + IN EFI_FILE *This, + OUT UINT64 *Position + ) +{ + SEMIHOST_FCB *Fcb; + + if ((This == NULL) || (Position == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS(This); + + *Position = Fcb->Position; + + return EFI_SUCCESS; +} + +/** + Set a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to set the requested position on. + @param[in] Position The byte position from the start of the file to set. + + @retval EFI_SUCCESS The position was set. + @retval EFI_DEVICE_ERROR The semi-hosting positioning operation failed. + @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open + directories. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. + +**/ +EFI_STATUS +FileSetPosition ( + IN EFI_FILE *This, + IN UINT64 Position + ) +{ + SEMIHOST_FCB *Fcb; + RETURN_STATUS Return; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (Fcb->IsRoot) { + if (Position != 0) { + return EFI_UNSUPPORTED; + } + } + else { + // + // UEFI Spec section 12.5: + // "Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to + // be set to the end of the file." + // + if (Position == 0xFFFFFFFFFFFFFFFF) { + Position = Fcb->Info.FileSize; + } + Return = SemihostFileSeek (Fcb->SemihostHandle, MIN (Position, Fcb->Info.FileSize)); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + } + + Fcb->Position = Position; + + return EFI_SUCCESS; +} + +/** + Return information about a file. + + @param[in] Fcb A pointer to the description of an open file. + @param[in out] BufferSize The size, in bytes, of Buffer. + @param[out] Buffer A pointer to the data buffer to return. Not NULL if + "*BufferSize" is greater than 0. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. + BufferSize has been updated with the size needed to + complete the request. +**/ +STATIC +EFI_STATUS +GetFileInfo ( + IN SEMIHOST_FCB *Fcb, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_FILE_INFO *Info = NULL; + UINTN NameSize = 0; + UINTN ResultSize; + UINTN Index; + + if (Fcb->IsRoot == TRUE) { + ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16); + } else { + NameSize = AsciiStrLen (Fcb->FileName) + 1; + ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16); + } + + if (*BufferSize < ResultSize) { + *BufferSize = ResultSize; + return EFI_BUFFER_TOO_SMALL; + } + + Info = Buffer; + + // Copy the current file info + CopyMem (Info, &Fcb->Info, SIZE_OF_EFI_FILE_INFO); + + // Fill in the structure + Info->Size = ResultSize; + + if (Fcb->IsRoot == TRUE) { + Info->FileName[0] = L'\0'; + } else { + for (Index = 0; Index < NameSize; Index++) { + Info->FileName[Index] = Fcb->FileName[Index]; + } + } + + *BufferSize = ResultSize; + + return EFI_SUCCESS; +} + +/** + Return information about a file system. + + @param[in] Fcb A pointer to the description of an open file + which belongs to the file system, the information + is requested for. + @param[in out] BufferSize The size, in bytes, of Buffer. + @param[out] Buffer A pointer to the data buffer to return. Not NULL if + "*BufferSize" is greater than 0. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. + BufferSize has been updated with the size needed to + complete the request. + +**/ +STATIC +EFI_STATUS +GetFilesystemInfo ( + IN SEMIHOST_FCB *Fcb, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_FILE_SYSTEM_INFO *Info; + EFI_STATUS Status; + UINTN ResultSize; + UINTN StringSize; + + StringSize = StrSize (mSemihostFsLabel); + ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StringSize; + + if (*BufferSize >= ResultSize) { + ZeroMem (Buffer, ResultSize); + Status = EFI_SUCCESS; + + Info = Buffer; + + Info->Size = ResultSize; + Info->ReadOnly = FALSE; + Info->VolumeSize = 0; + Info->FreeSpace = 0; + Info->BlockSize = 0; + + CopyMem (Info->VolumeLabel, mSemihostFsLabel, StringSize); + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = ResultSize; + return Status; +} + +/** + Return information about a file or a file system. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the requested information is for. + @param[in] InformationType The type identifier for the information being requested : + EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or + EFI_FILE_SYSTEM_VOLUME_LABEL_ID + @param[in out] BufferSize The size, in bytes, of Buffer. + @param[out] Buffer A pointer to the data buffer to return. The type of the + data inside the buffer is indicated by InformationType. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. + BufferSize has been updated with the size needed to + complete the request. + @retval EFI_INVALID_PARAMETER The parameter "This" or "InformationType" or "BufferSize" + is NULL or "Buffer" is NULL and "*Buffersize" is greater + than 0. + +**/ +EFI_STATUS +FileGetInfo ( + IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + SEMIHOST_FCB *Fcb; + EFI_STATUS Status; + UINTN ResultSize; + + if ((This == NULL) || + (InformationType == NULL) || + (BufferSize == NULL) || + ((Buffer == NULL) && (*BufferSize > 0)) ) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS(This); + + if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + Status = GetFilesystemInfo (Fcb, BufferSize, Buffer); + } else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + Status = GetFileInfo (Fcb, BufferSize, Buffer); + } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + ResultSize = StrSize (mSemihostFsLabel); + + if (*BufferSize >= ResultSize) { + CopyMem (Buffer, mSemihostFsLabel, ResultSize); + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = ResultSize; + } else { + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +/** + Set information about a file. + + @param[in] Fcb A pointer to the description of the open file. + @param[in] Info A pointer to the file information to write. + + @retval EFI_SUCCESS The information was set. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED The file is a read-only file or has been + opened in read-only mode and an attempt is + being made to modify a field other than + Attribute. + @retval EFI_WRITE_PROTECTED An attempt is being made to modify a + read-only attribute. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed. + +**/ +STATIC +EFI_STATUS +SetFileInfo ( + IN SEMIHOST_FCB *Fcb, + IN EFI_FILE_INFO *Info + ) +{ + EFI_STATUS Status; + RETURN_STATUS Return; + BOOLEAN FileSizeIsDifferent; + BOOLEAN FileNameIsDifferent; + BOOLEAN ReadOnlyIsDifferent; + CHAR8 *AsciiFileName; + UINTN FileSize; + UINTN Length; + UINTN SemihostHandle; + + // + // A directory can not be changed to a file and a file can + // not be changed to a directory. + // + if (((Info->Attribute & EFI_FILE_DIRECTORY) != 0) != Fcb->IsRoot) { + return EFI_ACCESS_DENIED; + } + + Length = StrLen (Info->FileName) + 1; + AsciiFileName = AllocatePool (Length); + if (AsciiFileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeStrToAsciiStrS (Info->FileName, AsciiFileName, Length); + + FileSizeIsDifferent = (Info->FileSize != Fcb->Info.FileSize); + FileNameIsDifferent = (AsciiStrCmp (AsciiFileName, Fcb->FileName) != 0); + ReadOnlyIsDifferent = CompareMem ( + &Info->CreateTime, + &Fcb->Info.CreateTime, + 3 * sizeof (EFI_TIME) + ) != 0; + + // + // For a read-only file or a file opened in read-only mode, only + // the Attribute field can be modified. As the root directory is + // read-only (i.e. VolumeOpen()), this protects the root directory + // description. + // + if ((Fcb->OpenMode == EFI_FILE_MODE_READ) || + (Fcb->Info.Attribute & EFI_FILE_READ_ONLY) ) { + if (FileSizeIsDifferent || FileNameIsDifferent || ReadOnlyIsDifferent) { + Status = EFI_ACCESS_DENIED; + goto Error; + } + } + + if (ReadOnlyIsDifferent) { + Status = EFI_WRITE_PROTECTED; + goto Error; + } + + Status = EFI_DEVICE_ERROR; + + if (FileSizeIsDifferent) { + FileSize = Info->FileSize; + if (Fcb->Info.FileSize < FileSize) { + Status = ExtendFile (Fcb, FileSize - Fcb->Info.FileSize); + if (EFI_ERROR (Status)) { + goto Error; + } + // + // The read/write position from the host file system point of view + // is at the end of the file. If the position from this module + // point of view is smaller than the new file size, then + // ask the host file system to move to that position. + // + if (Fcb->Position < FileSize) { + FileSetPosition (&Fcb->File, Fcb->Position); + } + } + Fcb->Info.FileSize = FileSize; + + Return = SemihostFileLength (Fcb->SemihostHandle, &Length); + if (RETURN_ERROR (Return)) { + goto Error; + } + Fcb->Info.PhysicalSize = Length; + } + + // + // Note down in RAM the Attribute field but we can not ask + // for its modification to the host file system as the + // semi-host interface does not provide this feature. + // + Fcb->Info.Attribute = Info->Attribute; + + if (FileNameIsDifferent) { + Return = SemihostFileOpen ( + AsciiFileName, + SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY, + &SemihostHandle + ); + if (!RETURN_ERROR (Return)) { + SemihostFileClose (SemihostHandle); + Status = EFI_ACCESS_DENIED; + goto Error; + } + + Return = SemihostFileRename (Fcb->FileName, AsciiFileName); + if (RETURN_ERROR (Return)) { + goto Error; + } + FreePool (Fcb->FileName); + Fcb->FileName = AsciiFileName; + AsciiFileName = NULL; + } + + Status = EFI_SUCCESS; + +Error: + if (AsciiFileName != NULL) { + FreePool (AsciiFileName); + } + + return Status; +} + +/** + Set information about a file or a file system. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the information is for. + @param[in] InformationType The type identifier for the information being set : + EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or + EFI_FILE_SYSTEM_VOLUME_LABEL_ID + @param[in] BufferSize The size, in bytes, of Buffer. + @param[in] Buffer A pointer to the data buffer to write. The type of the + data inside the buffer is indicated by InformationType. + + @retval EFI_SUCCESS The information was set. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and + the file is a read-only file or has been + opened in read-only mode and an attempt is + being made to modify a field other than + Attribute. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_WRITE_PROTECTED An attempt is being made to modify a + read-only attribute. + @retval EFI_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by + the data inside the buffer. + @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request failed. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ +EFI_STATUS +FileSetInfo ( + IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + SEMIHOST_FCB *Fcb; + EFI_FILE_INFO *Info; + EFI_FILE_SYSTEM_INFO *SystemInfo; + CHAR16 *VolumeLabel; + + if ((This == NULL) || (InformationType == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + Info = Buffer; + if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) { + return EFI_INVALID_PARAMETER; + } + if (BufferSize < Info->Size) { + return EFI_BAD_BUFFER_SIZE; + } + return SetFileInfo (Fcb, Info); + } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + SystemInfo = Buffer; + if (SystemInfo->Size < + (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) { + return EFI_INVALID_PARAMETER; + } + if (BufferSize < SystemInfo->Size) { + return EFI_BAD_BUFFER_SIZE; + } + Buffer = SystemInfo->VolumeLabel; + + if (StrSize (Buffer) > 0) { + VolumeLabel = AllocateCopyPool (StrSize (Buffer), Buffer); + if (VolumeLabel != NULL) { + FreePool (mSemihostFsLabel); + mSemihostFsLabel = VolumeLabel; + return EFI_SUCCESS; + } else { + return EFI_OUT_OF_RESOURCES; + } + } else { + return EFI_INVALID_PARAMETER; + } + } else if (!CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + return EFI_UNSUPPORTED; + } else { + return EFI_UNSUPPORTED; + } +} + +EFI_STATUS +FileFlush ( + IN EFI_FILE *File + ) +{ + SEMIHOST_FCB *Fcb; + + Fcb = SEMIHOST_FCB_FROM_THIS(File); + + if (Fcb->IsRoot) { + return EFI_SUCCESS; + } else { + if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY) + || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) { + return EFI_ACCESS_DENIED; + } else { + return EFI_SUCCESS; + } + } +} + +EFI_STATUS +SemihostFsEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EFI_NOT_FOUND; + + if (SemihostConnectionSupported ()) { + mSemihostFsLabel = AllocateCopyPool (StrSize (DEFAULT_SEMIHOST_FS_LABEL), DEFAULT_SEMIHOST_FS_LABEL); + if (mSemihostFsLabel == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &gInstallHandle, + &gEfiSimpleFileSystemProtocolGuid, &gSemihostFs, + &gEfiDevicePathProtocolGuid, &gDevicePath, + NULL + ); + + if (EFI_ERROR(Status)) { + FreePool (mSemihostFsLabel); + } + } + + return Status; +} -- cgit