/** Set information about a file or a volume. @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 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 InformationType is EFI_FILE_INFO_ID and the file was 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_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by the data inside the buffer. @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed. @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. **/ EFIAPI EFI_STATUS BootMonFsSetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN UINTN BufferSize, IN VOID *Buffer ) { BOOTMON_FS_FILE *File; EFI_FILE_INFO *Info; EFI_FILE_SYSTEM_INFO *SystemInfo; if ((This == NULL) || (InformationType == NULL) || (Buffer == NULL) ) { return EFI_INVALID_PARAMETER; } File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (File->Info == NULL) { return EFI_INVALID_PARAMETER; } 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 (File->Instance, File, Info)); } // // The only writable field in the other two information types // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the // filesystem volume label. This can be retrieved with GetInfo, but it is // hard-coded into this driver, not stored on media. // 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; } return EFI_WRITE_PROTECTED; } if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { return EFI_WRITE_PROTECTED; } return EFI_UNSUPPORTED; }
EFIAPI EFI_STATUS BootMonFsDelete ( IN EFI_FILE_PROTOCOL *This ) { EFI_STATUS Status; BOOTMON_FS_FILE *File; LIST_ENTRY *RegionToFlushLink; BOOTMON_FS_FILE_REGION *Region; HW_IMAGE_DESCRIPTION *Description; EFI_BLOCK_IO_PROTOCOL *BlockIo; UINT8 *EmptyBuffer; File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (File == NULL) { return EFI_DEVICE_ERROR; } Status = EFI_SUCCESS; if (BootMonFsFileNeedFlush (File)) { // Free the entries from the Buffer List RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink); do { Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink; // Get Next entry RegionToFlushLink = RemoveEntryList (RegionToFlushLink); // Free the buffers FreePool (Region->Buffer); FreePool (Region); } while (!IsListEmpty (&File->RegionToFlushLink)); } // If (RegionCount is greater than 0) then the file already exists if (File->HwDescription.RegionCount > 0) { Description = &File->HwDescription; BlockIo = File->Instance->BlockIo; // Create an empty buffer EmptyBuffer = AllocateZeroPool (BlockIo->Media->BlockSize); if (EmptyBuffer == NULL) { FreePool (File); return EFI_OUT_OF_RESOURCES; } // Invalidate the last Block Status = BlockIo->WriteBlocks (BlockIo, BlockIo->Media->MediaId, Description->BlockEnd, BlockIo->Media->BlockSize, EmptyBuffer); ASSERT_EFI_ERROR (Status); FreePool (EmptyBuffer); } // Remove the entry from the list RemoveEntryList (&File->Link); FreePool (File); return Status; }
EFIAPI EFI_STATUS BootMonFsSetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN UINTN BufferSize, IN VOID *Buffer ) { EFI_STATUS Status; BOOTMON_FS_FILE *File; BOOTMON_FS_INSTANCE *Instance; File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (File == NULL) { return EFI_DEVICE_ERROR; } Instance = File->Instance; if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) { Status = SetFileInfo (Instance, File, BufferSize, (EFI_FILE_INFO *) Buffer); } else { // The only writable field in the other two information types // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the // filesystem volume label. This can be retrieved with GetInfo, but it is // hard-coded into this driver, not stored on media. Status = EFI_UNSUPPORTED; } return Status; }
EFIAPI EFI_STATUS BootMonFsReadDirectory ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { BOOTMON_FS_INSTANCE *Instance; BOOTMON_FS_FILE *RootFile; BOOTMON_FS_FILE *File; EFI_FILE_INFO *Info; UINTN NameSize; UINTN ResultSize; EFI_STATUS Status; UINTN Index; RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (RootFile == NULL) { return EFI_INVALID_PARAMETER; } Instance = RootFile->Instance; Status = BootMonGetFileFromPosition (Instance, RootFile->Position, &File); if (EFI_ERROR (Status)) { // No more file *BufferSize = 0; return EFI_SUCCESS; } NameSize = AsciiStrLen (File->HwDescription.Footer.Filename) + 1; ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16)); if (*BufferSize < ResultSize) { *BufferSize = ResultSize; return EFI_BUFFER_TOO_SMALL; } // Zero out the structure Info = Buffer; ZeroMem (Info, ResultSize); // Fill in the structure Info->Size = ResultSize; Info->FileSize = BootMonFsGetImageLength (File); Info->PhysicalSize = BootMonFsGetPhysicalSize (File); for (Index = 0; Index < NameSize; Index++) { Info->FileName[Index] = File->HwDescription.Footer.Filename[Index]; } *BufferSize = ResultSize; RootFile->Position++; return EFI_SUCCESS; }
EFIAPI EFI_STATUS BootMonFsGetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { EFI_STATUS Status; BOOTMON_FS_FILE *File; BOOTMON_FS_INSTANCE *Instance; if ((This == NULL) || (InformationType == NULL) || (BufferSize == NULL) || ((Buffer == NULL) && (*BufferSize > 0)) ) { return EFI_INVALID_PARAMETER; } File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (File->Info == NULL) { return EFI_INVALID_PARAMETER; } Instance = File->Instance; // If the instance has not been initialized yet then do it ... if (!Instance->Initialized) { Status = BootMonFsInitialize (Instance); } else { Status = EFI_SUCCESS; } if (!EFI_ERROR (Status)) { if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid) != 0) { Status = GetFileSystemVolumeLabelInfo (Instance, BufferSize, Buffer); } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) { Status = GetFilesystemInfo (Instance, BufferSize, Buffer); } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) { Status = GetFileInfo (Instance, File, BufferSize, Buffer); } else if (CompareGuid (InformationType, &gArmBootMonFsFileInfoGuid) != 0) { Status = GetBootMonFsFileInfo (Instance, File, BufferSize, Buffer); } else { Status = EFI_UNSUPPORTED; } } return Status; }
EFIAPI EFI_STATUS BootMonFsFlushDirectory ( IN EFI_FILE_PROTOCOL *This ) { BOOTMON_FS_FILE *RootFile; LIST_ENTRY *ListFiles; LIST_ENTRY *Link; BOOTMON_FS_FILE *File; RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (RootFile == NULL) { return EFI_INVALID_PARAMETER; } ListFiles = &RootFile->Link; if (IsListEmpty (ListFiles)) { return EFI_SUCCESS; } // // Flush all the files that need to be flushed // // Go through all the list of files to flush them for (Link = GetFirstNode (ListFiles); !IsNull (ListFiles, Link); Link = GetNextNode (ListFiles, Link) ) { File = BOOTMON_FS_FILE_FROM_LINK_THIS (Link); File->File.Flush (&File->File); } return EFI_SUCCESS; }
EFIAPI EFI_STATUS BootMonFsSetDirPosition ( IN EFI_FILE_PROTOCOL *This, IN UINT64 Position ) { BOOTMON_FS_FILE *File; File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (File == NULL) { return EFI_INVALID_PARAMETER; } // UEFI Spec section 12.5: // "The seek request for nonzero is not valid on open directories." if (Position != 0) { return EFI_UNSUPPORTED; } File->Position = Position; return EFI_SUCCESS; }
/** Opens a file on the Nor Flash FS volume Calls BootMonFsGetFileFromAsciiFilename to search the list of tracked files. @param This The EFI_FILE_PROTOCOL parent handle. @param NewHandle Double-pointer to the newly created protocol. @param FileName The name of the image/metadata on flash @param OpenMode Read,write,append etc @param Attributes ? @return EFI_STATUS OUT_OF_RESOURCES Run out of space to keep track of the allocated structures DEVICE_ERROR Unable to locate the volume associated with the parent file handle NOT_FOUND Filename wasn't found on flash SUCCESS **/ EFIAPI EFI_STATUS BootMonFsOpenFile ( IN EFI_FILE_PROTOCOL *This, OUT EFI_FILE_PROTOCOL **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes ) { BOOTMON_FS_FILE *Directory; BOOTMON_FS_FILE *File; BOOTMON_FS_INSTANCE *Instance; CHAR8* AsciiFileName; EFI_STATUS Status; if ((FileName == NULL) || (NewHandle == NULL)) { return EFI_INVALID_PARAMETER; } // The only valid modes are read, read/write, and read/write/create if (!(OpenMode & EFI_FILE_MODE_READ) || ((OpenMode & EFI_FILE_MODE_CREATE) && !(OpenMode & EFI_FILE_MODE_WRITE))) { return EFI_INVALID_PARAMETER; } Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (Directory == NULL) { return EFI_DEVICE_ERROR; } Instance = Directory->Instance; // If the instance has not been initialized it yet then do it ... if (!Instance->Initialized) { Status = BootMonFsInitialize (Instance); if (EFI_ERROR (Status)) { return Status; } } // BootMonFs interface requires ASCII filenames AsciiFileName = AllocatePool ((StrLen (FileName) + 1) * sizeof (CHAR8)); if (AsciiFileName == NULL) { return EFI_OUT_OF_RESOURCES; } UnicodeStrToAsciiStr (FileName, AsciiFileName); if ((AsciiStrCmp (AsciiFileName, "\\") == 0) || (AsciiStrCmp (AsciiFileName, "/") == 0) || (AsciiStrCmp (AsciiFileName, "") == 0) || (AsciiStrCmp (AsciiFileName, ".") == 0)) { // // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory // *NewHandle = &Instance->RootFile->File; Instance->RootFile->Position = 0; Status = EFI_SUCCESS; } else { // // Open or Create a regular file // // Check if the file already exists Status = BootMonGetFileFromAsciiFileName (Instance, AsciiFileName, &File); if (Status == EFI_NOT_FOUND) { // The file doesn't exist. if (OpenMode & EFI_FILE_MODE_CREATE) { // If the file does not exist but is required then create it. if (Attributes & EFI_FILE_DIRECTORY) { // BootMonFS doesn't support subdirectories Status = EFI_UNSUPPORTED; } else { // Create a new file Status = CreateNewFile (Instance, AsciiFileName, &File); if (!EFI_ERROR (Status)) { File->OpenMode = OpenMode; *NewHandle = &File->File; File->Position = 0; } } } } else if (Status == EFI_SUCCESS) { // The file exists File->OpenMode = OpenMode; *NewHandle = &File->File; File->Position = 0; } } FreePool (AsciiFileName); return Status; }
EFIAPI EFI_STATUS BootMonFsFlushFile ( IN EFI_FILE_PROTOCOL *This ) { EFI_STATUS Status; BOOTMON_FS_INSTANCE *Instance; LIST_ENTRY *RegionToFlushLink; BOOTMON_FS_FILE *File; BOOTMON_FS_FILE *NextFile; BOOTMON_FS_FILE_REGION *Region; LIST_ENTRY *FileLink; UINTN CurrentPhysicalSize; UINTN BlockSize; UINT64 FileStart; UINT64 FileEnd; UINT64 RegionStart; UINT64 RegionEnd; UINT64 NewFileSize; UINT64 EndOfAppendSpace; BOOLEAN HasSpace; EFI_DISK_IO_PROTOCOL *DiskIo; EFI_BLOCK_IO_PROTOCOL *BlockIo; Status = EFI_SUCCESS; FileStart = 0; File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); if (File == NULL) { return EFI_INVALID_PARAMETER; } // Check if the file needs to be flushed if (!BootMonFsFileNeedFlush (File)) { return Status; } Instance = File->Instance; BlockIo = Instance->BlockIo; DiskIo = Instance->DiskIo; BlockSize = BlockIo->Media->BlockSize; // If the file doesn't exist then find a space for it if (File->HwDescription.RegionCount == 0) { Status = BootMonFsFindSpaceForNewFile (File, &FileStart); // FileStart has changed so we need to recompute RegionEnd if (EFI_ERROR (Status)) { return Status; } } else { FileStart = File->HwDescription.BlockStart * BlockSize; } // FileEnd is the NOR address of the end of the file's data FileEnd = FileStart + BootMonFsGetImageLength (File); for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink); !IsNull (&File->RegionToFlushLink, RegionToFlushLink); RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink) ) { Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink; // RegionStart and RegionEnd are the the intended NOR address of the // start and end of the region RegionStart = FileStart + Region->Offset; RegionEnd = RegionStart + Region->Size; if (RegionEnd < FileEnd) { // Handle regions representing edits to existing portions of the file // Write the region data straight into the file Status = DiskIo->WriteDisk (DiskIo, BlockIo->Media->MediaId, RegionStart, Region->Size, Region->Buffer ); if (EFI_ERROR (Status)) { return Status; } } else { // Handle regions representing appends to the file // // Note: Since seeking past the end of the file with SetPosition() is // valid, it's possible there will be a gap between the current end of // the file and the beginning of the new region. Since the UEFI spec // says nothing about this case (except "a subsequent write would grow // the file"), we just leave garbage in the gap. // Check if there is space to append the new region HasSpace = FALSE; NewFileSize = (RegionEnd - FileStart) + sizeof (HW_IMAGE_DESCRIPTION); CurrentPhysicalSize = BootMonFsGetPhysicalSize (File); if (NewFileSize <= CurrentPhysicalSize) { HasSpace = TRUE; } else { // Get the File Description for the next file FileLink = GetNextNode (&Instance->RootFile->Link, &File->Link); if (!IsNull (&Instance->RootFile->Link, FileLink)) { NextFile = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink); // If there is space between the beginning of the current file and the // beginning of the next file then use it EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize; } else { // We are flushing the last file. EndOfAppendSpace = (BlockIo->Media->LastBlock + 1) * BlockSize; } if (EndOfAppendSpace - FileStart >= NewFileSize) { HasSpace = TRUE; } } if (HasSpace == TRUE) { Status = FlushAppendRegion (File, Region, NewFileSize, FileStart); if (EFI_ERROR (Status)) { return Status; } } else { // There isn't a space for the file. // Options here are to move the file or fragment it. However as files // may represent boot images at fixed positions, these options will // break booting if the bootloader doesn't use BootMonFs to find the // image. return EFI_VOLUME_FULL; } } } FreeFileRegions (File); // Flush DiskIo Buffers (see UEFI Spec 12.7 - DiskIo buffers are flushed by // calling FlushBlocks on the same device's BlockIo). BlockIo->FlushBlocks (BlockIo); return Status; }