Files
system76-edk2/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c
Olivier Martin 94e0955d3e ArmPlatformPkg/BootMonFs: Added support for the NorFlash File System of the ARM Development Boards
This is the filesystem created by the microcontroller on NOR Flash of the ARM Versatile
Express Development Board.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.martin@arm.com>



git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15126 6f19259b-4bc3-4df7-8a09-765794883524
2014-01-16 00:06:13 +00:00

603 lines
16 KiB
C

/** @file
*
* Copyright (c) 2012-2014, ARM Limited. All rights reserved.
*
* This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License
* which accompanies this distribution. The full text of the license may be found at
* http://opensource.org/licenses/bsd-license.php
*
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*
**/
#include "BootMonFsInternal.h"
EFIAPI
EFI_STATUS
OpenBootMonFsOpenVolume (
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
OUT EFI_FILE_PROTOCOL **Root
)
{
BOOTMON_FS_INSTANCE *Instance;
Instance = BOOTMON_FS_FROM_FS_THIS (This);
if (Instance == NULL) {
return EFI_DEVICE_ERROR;
}
*Root = &Instance->RootFile->File;
return EFI_SUCCESS;
}
UINT32
BootMonFsGetImageLength (
IN BOOTMON_FS_FILE *File
)
{
UINT32 Index;
UINT32 FileSize;
LIST_ENTRY *RegionToFlushLink;
BOOTMON_FS_FILE_REGION *Region;
FileSize = 0;
// Look at all Flash areas to determine file size
for (Index = 0; Index < HW_IMAGE_DESCRIPTION_REGION_MAX; Index++) {
FileSize += File->HwDescription.Region[Index].Size;
}
// Add the regions that have not been flushed yet
for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
!IsNull (&File->RegionToFlushLink, RegionToFlushLink);
RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)
)
{
Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
if (Region->Offset + Region->Size > FileSize) {
FileSize += Region->Offset + Region->Size;
}
}
return FileSize;
}
UINTN
BootMonFsGetPhysicalSize (
IN BOOTMON_FS_FILE* File
)
{
// Return 0 for files that haven't yet been flushed to media
if (File->HwDescription.RegionCount == 0) {
return 0;
}
return ((File->HwDescription.BlockEnd - File->HwDescription.BlockStart) + 1 )
* File->Instance->Media->BlockSize;
}
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;
}
EFI_STATUS
BootMonFsOpenDirectory (
OUT EFI_FILE_PROTOCOL **NewHandle,
IN CHAR16 *FileName,
IN BOOTMON_FS_INSTANCE *Volume
)
{
ASSERT(0);
return EFI_UNSUPPORTED;
}
EFI_STATUS
GetFileSystemVolumeLabelInfo (
IN BOOTMON_FS_INSTANCE *Instance,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
UINTN Size;
EFI_FILE_SYSTEM_VOLUME_LABEL *Label;
EFI_STATUS Status;
Label = Buffer;
// Value returned by StrSize includes null terminator.
Size = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL
+ StrSize (Instance->FsInfo.VolumeLabel);
if (*BufferSize >= Size) {
CopyMem (&Label->VolumeLabel, &Instance->FsInfo.VolumeLabel, Size);
Status = EFI_SUCCESS;
} else {
Status = EFI_BUFFER_TOO_SMALL;
}
*BufferSize = Size;
return Status;
}
// Helper function that calculates a rough "free space" by:
// - Taking the media size
// - Subtracting the sum of all file sizes
// - Subtracting the block size times the number of files
// (To account for the blocks containing the HW_IMAGE_INFO
STATIC
UINT64
ComputeFreeSpace (
IN BOOTMON_FS_INSTANCE *Instance
)
{
LIST_ENTRY *FileLink;
UINT64 FileSizeSum;
UINT64 MediaSize;
UINTN NumFiles;
EFI_BLOCK_IO_MEDIA *Media;
BOOTMON_FS_FILE *File;
Media = Instance->BlockIo->Media;
MediaSize = Media->BlockSize * (Media->LastBlock + 1);
NumFiles = 0;
FileSizeSum = 0;
for (FileLink = GetFirstNode (&Instance->RootFile->Link);
!IsNull (&Instance->RootFile->Link, FileLink);
FileLink = GetNextNode (&Instance->RootFile->Link, FileLink)
)
{
File = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
FileSizeSum += BootMonFsGetImageLength (File);
NumFiles++;
}
return MediaSize - (FileSizeSum + (Media->BlockSize + NumFiles));
}
EFI_STATUS
GetFilesystemInfo (
IN BOOTMON_FS_INSTANCE *Instance,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
if (*BufferSize >= Instance->FsInfo.Size) {
Instance->FsInfo.FreeSpace = ComputeFreeSpace (Instance);
CopyMem (Buffer, &Instance->FsInfo, Instance->FsInfo.Size);
Status = EFI_SUCCESS;
} else {
Status = EFI_BUFFER_TOO_SMALL;
}
*BufferSize = Instance->FsInfo.Size;
return Status;
}
EFI_STATUS
GetFileInfo (
IN BOOTMON_FS_INSTANCE *Instance,
IN BOOTMON_FS_FILE *File,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
EFI_FILE_INFO *Info;
UINTN ResultSize;
UINTN NameSize;
UINTN Index;
if (File == Instance->RootFile) {
NameSize = 0;
ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof (CHAR16);
} else {
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;
}
Info = Buffer;
// Zero out the structure
ZeroMem (Info, ResultSize);
// Fill in the structure
Info->Size = ResultSize;
if (File == Instance->RootFile) {
Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
Info->FileName[0] = L'\0';
} else {
Info->FileSize = BootMonFsGetImageLength (File);
Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
for (Index = 0; Index < NameSize; Index++) {
Info->FileName[Index] = File->HwDescription.Footer.Filename[Index];
}
}
*BufferSize = ResultSize;
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
SetFileName (
IN BOOTMON_FS_FILE *File,
IN CHAR16 *FileNameUnicode
)
{
CHAR8 *FileNameAscii;
UINT16 SavedChar;
UINTN FileNameSize;
BOOTMON_FS_FILE *SameFile;
EFI_STATUS Status;
// EFI Shell inserts '\' in front of the filename that must be stripped
if (FileNameUnicode[0] == L'\\') {
FileNameUnicode++;
}
//
// Convert Unicode into Ascii
//
SavedChar = L'\0';
FileNameSize = StrLen (FileNameUnicode) + 1;
FileNameAscii = AllocatePool (FileNameSize * sizeof (CHAR8));
if (FileNameAscii == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// If Unicode string is too long then truncate it.
if (FileNameSize > MAX_NAME_LENGTH) {
SavedChar = FileNameUnicode[MAX_NAME_LENGTH - 1];
FileNameUnicode[MAX_NAME_LENGTH - 1] = L'\0';
}
UnicodeStrToAsciiStr (FileNameUnicode, FileNameAscii);
// If the unicode string was truncated then restore its original content.
if (SavedChar != L'\0') {
FileNameUnicode[MAX_NAME_LENGTH - 1] = SavedChar;
}
// If we're changing the file name
if (AsciiStrCmp (FileNameAscii, File->HwDescription.Footer.Filename)) {
// Check a file with that filename doesn't already exist
if (BootMonGetFileFromAsciiFileName (
File->Instance,
File->HwDescription.Footer.Filename,
&SameFile) != EFI_NOT_FOUND) {
Status = EFI_ACCESS_DENIED;
} else {
AsciiStrCpy (FileNameAscii, File->HwDescription.Footer.Filename);
Status = EFI_SUCCESS;
}
} else {
// No change to filename
Status = EFI_SUCCESS;
}
FreePool (FileNameAscii);
return Status;
}
// Set the file's size (NB "size", not "physical size"). If the change amounts
// to an increase, simply do a write followed by a flush.
// (This is a helper function for SetFileInfo.)
STATIC
EFI_STATUS
SetFileSize (
IN BOOTMON_FS_INSTANCE *Instance,
IN BOOTMON_FS_FILE *BootMonFsFile,
IN UINTN Size
)
{
UINT64 StoredPosition;
EFI_STATUS Status;
EFI_FILE_PROTOCOL *File;
CHAR8 Buffer;
UINTN BufferSize;
Buffer = 0;
BufferSize = sizeof (Buffer);
File = &BootMonFsFile->File;
if (!(BootMonFsFile->OpenMode & EFI_FILE_MODE_WRITE)) {
return EFI_ACCESS_DENIED;
}
if (Size <= BootMonFsFile->HwDescription.Region[0].Size) {
BootMonFsFile->HwDescription.Region[0].Size = Size;
} else {
// Increasing a file's size is potentially complicated as it may require
// moving the image description on media. The simplest way to do it is to
// seek past the end of the file (which is valid in UEFI) and perform a
// Write.
// Save position
Status = File->GetPosition (File, &StoredPosition);
if (EFI_ERROR (Status)) {
return Status;
}
Status = File->SetPosition (File, Size - 1);
if (EFI_ERROR (Status)) {
return Status;
}
Status = File->Write (File, &BufferSize, &Buffer);
if (EFI_ERROR (Status)) {
return Status;
}
// Restore saved position
Status = File->SetPosition (File, Size - 1);
if (EFI_ERROR (Status)) {
return Status;
}
Status = File->Flush (File);
if (EFI_ERROR (Status)) {
return Status;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
SetFileInfo (
IN BOOTMON_FS_INSTANCE *Instance,
IN BOOTMON_FS_FILE *File,
IN UINTN BufferSize,
IN EFI_FILE_INFO *Info
)
{
EFI_STATUS Status;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
UINT8 *DataBuffer;
UINTN BlockSize;
Status = EFI_SUCCESS;
BlockIo = Instance->BlockIo;
// Note that a call to this function on a file opened read-only is only
// invalid if it actually changes fields, so we don't immediately fail if the
// OpenMode is wrong.
// Also note that the only fields supported are filename and size, others are
// ignored.
if (File != Instance->RootFile) {
if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) {
return EFI_ACCESS_DENIED;
}
SetFileName (File, Info->FileName);
if (EFI_ERROR (Status)) {
return Status;
}
// Update file size
Status = SetFileSize (Instance, File, Info->FileSize);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Update the last block
//
BlockSize = BlockIo->Media->BlockSize;
DataBuffer = AllocatePool (BlockSize);
if (DataBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = BlockIo->ReadBlocks (BlockIo, Instance->Media->MediaId,
File->HwDescription.BlockEnd, BlockSize, DataBuffer);
if (EFI_ERROR (Status)) {
FreePool (DataBuffer);
return Status;
}
CopyMem (DataBuffer + BlockSize - sizeof (File->HwDescription), &File->HwDescription, sizeof (File->HwDescription));
Status = BlockIo->WriteBlocks (BlockIo, Instance->Media->MediaId,
File->HwDescription.BlockEnd, BlockSize, DataBuffer);
FreePool (DataBuffer);
}
return Status;
}
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;
File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
if (File == NULL) {
return EFI_DEVICE_ERROR;
}
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 {
Status = EFI_UNSUPPORTED;
}
}
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
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;
}