3 * Copyright (c) 2012-2014, ARM Limited. All rights reserved.
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include "BootMonFsInternal.h"
19 OpenBootMonFsOpenVolume (
20 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*This
,
21 OUT EFI_FILE_PROTOCOL
**Root
24 BOOTMON_FS_INSTANCE
*Instance
;
26 Instance
= BOOTMON_FS_FROM_FS_THIS (This
);
27 if (Instance
== NULL
) {
28 return EFI_DEVICE_ERROR
;
31 Instance
->RootFile
->Info
->Attribute
= EFI_FILE_READ_ONLY
| EFI_FILE_DIRECTORY
;
33 *Root
= &Instance
->RootFile
->File
;
39 BootMonFsGetImageLength (
40 IN BOOTMON_FS_FILE
*File
45 LIST_ENTRY
*RegionToFlushLink
;
46 BOOTMON_FS_FILE_REGION
*Region
;
50 // Look at all Flash areas to determine file size
51 for (Index
= 0; Index
< HW_IMAGE_DESCRIPTION_REGION_MAX
; Index
++) {
52 FileSize
+= File
->HwDescription
.Region
[Index
].Size
;
55 // Add the regions that have not been flushed yet
56 for (RegionToFlushLink
= GetFirstNode (&File
->RegionToFlushLink
);
57 !IsNull (&File
->RegionToFlushLink
, RegionToFlushLink
);
58 RegionToFlushLink
= GetNextNode (&File
->RegionToFlushLink
, RegionToFlushLink
)
61 Region
= (BOOTMON_FS_FILE_REGION
*)RegionToFlushLink
;
62 if (Region
->Offset
+ Region
->Size
> FileSize
) {
63 FileSize
+= Region
->Offset
+ Region
->Size
;
71 BootMonFsGetPhysicalSize (
72 IN BOOTMON_FS_FILE
* File
75 // Return 0 for files that haven't yet been flushed to media
76 if (File
->HwDescription
.RegionCount
== 0) {
80 return ((File
->HwDescription
.BlockEnd
- File
->HwDescription
.BlockStart
) + 1 )
81 * File
->Instance
->Media
->BlockSize
;
86 BootMonFsSetDirPosition (
87 IN EFI_FILE_PROTOCOL
*This
,
91 BOOTMON_FS_FILE
*File
;
93 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
95 return EFI_INVALID_PARAMETER
;
98 // UEFI Spec section 12.5:
99 // "The seek request for nonzero is not valid on open directories."
101 return EFI_UNSUPPORTED
;
103 File
->Position
= Position
;
109 BootMonFsOpenDirectory (
110 OUT EFI_FILE_PROTOCOL
**NewHandle
,
112 IN BOOTMON_FS_INSTANCE
*Volume
117 return EFI_UNSUPPORTED
;
122 GetFileSystemVolumeLabelInfo (
123 IN BOOTMON_FS_INSTANCE
*Instance
,
124 IN OUT UINTN
*BufferSize
,
129 EFI_FILE_SYSTEM_VOLUME_LABEL
*Label
;
134 // Value returned by StrSize includes null terminator.
135 Size
= SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL
136 + StrSize (Instance
->FsInfo
.VolumeLabel
);
138 if (*BufferSize
>= Size
) {
139 CopyMem (&Label
->VolumeLabel
, &Instance
->FsInfo
.VolumeLabel
, Size
);
140 Status
= EFI_SUCCESS
;
142 Status
= EFI_BUFFER_TOO_SMALL
;
148 // Helper function that calculates a rough "free space" by:
149 // - Taking the media size
150 // - Subtracting the sum of all file sizes
151 // - Subtracting the block size times the number of files
152 // (To account for the blocks containing the HW_IMAGE_INFO
156 IN BOOTMON_FS_INSTANCE
*Instance
159 LIST_ENTRY
*FileLink
;
163 EFI_BLOCK_IO_MEDIA
*Media
;
164 BOOTMON_FS_FILE
*File
;
166 Media
= Instance
->BlockIo
->Media
;
167 MediaSize
= Media
->BlockSize
* (Media
->LastBlock
+ 1);
171 for (FileLink
= GetFirstNode (&Instance
->RootFile
->Link
);
172 !IsNull (&Instance
->RootFile
->Link
, FileLink
);
173 FileLink
= GetNextNode (&Instance
->RootFile
->Link
, FileLink
)
176 File
= BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink
);
177 FileSizeSum
+= BootMonFsGetImageLength (File
);
182 return MediaSize
- (FileSizeSum
+ (Media
->BlockSize
+ NumFiles
));
188 IN BOOTMON_FS_INSTANCE
*Instance
,
189 IN OUT UINTN
*BufferSize
,
195 if (*BufferSize
>= Instance
->FsInfo
.Size
) {
196 Instance
->FsInfo
.FreeSpace
= ComputeFreeSpace (Instance
);
197 CopyMem (Buffer
, &Instance
->FsInfo
, Instance
->FsInfo
.Size
);
198 Status
= EFI_SUCCESS
;
200 Status
= EFI_BUFFER_TOO_SMALL
;
203 *BufferSize
= Instance
->FsInfo
.Size
;
210 IN BOOTMON_FS_INSTANCE
*Instance
,
211 IN BOOTMON_FS_FILE
*File
,
212 IN OUT UINTN
*BufferSize
,
219 ResultSize
= SIZE_OF_EFI_FILE_INFO
+ StrSize (File
->Info
->FileName
);
221 if (*BufferSize
< ResultSize
) {
222 *BufferSize
= ResultSize
;
223 return EFI_BUFFER_TOO_SMALL
;
228 CopyMem (Info
, File
->Info
, ResultSize
);
229 // Size of the information
230 Info
->Size
= ResultSize
;
232 *BufferSize
= ResultSize
;
239 GetBootMonFsFileInfo (
240 IN BOOTMON_FS_INSTANCE
*Instance
,
241 IN BOOTMON_FS_FILE
*File
,
242 IN OUT UINTN
*BufferSize
,
247 BOOTMON_FS_FILE_INFO
*Info
;
251 if (File
== Instance
->RootFile
) {
252 Status
= EFI_UNSUPPORTED
;
254 ResultSize
= SIZE_OF_BOOTMON_FS_FILE_INFO
;
256 if (*BufferSize
< ResultSize
) {
257 *BufferSize
= ResultSize
;
258 Status
= EFI_BUFFER_TOO_SMALL
;
262 // Zero out the structure
263 ZeroMem (Info
, ResultSize
);
265 // Fill in the structure
266 Info
->Size
= ResultSize
;
268 Info
->EntryPoint
= File
->HwDescription
.EntryPoint
;
269 Info
->RegionCount
= File
->HwDescription
.RegionCount
;
270 for (Index
= 0; Index
< File
->HwDescription
.RegionCount
; Index
++) {
271 Info
->Region
[Index
].LoadAddress
= File
->HwDescription
.Region
[Index
].LoadAddress
;
272 Info
->Region
[Index
].Size
= File
->HwDescription
.Region
[Index
].Size
;
273 Info
->Region
[Index
].Offset
= File
->HwDescription
.Region
[Index
].Offset
;
274 Info
->Region
[Index
].Checksum
= File
->HwDescription
.Region
[Index
].Checksum
;
276 *BufferSize
= ResultSize
;
277 Status
= EFI_SUCCESS
;
285 Set the name of a file.
287 This is a helper function for SetFileInfo().
289 @param[in] Instance A pointer to the description of the volume
291 @param[in] File A pointer to the description of the file.
292 @param[in] FileName A pointer to the new name of the file.
294 @retval EFI_SUCCESS The name was set.
295 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file
296 to a file that is already present.
302 IN BOOTMON_FS_INSTANCE
*Instance
,
303 IN BOOTMON_FS_FILE
*File
,
304 IN CONST CHAR16
*FileName
307 CHAR16 TruncFileName
[MAX_NAME_LENGTH
];
308 CHAR8 AsciiFileName
[MAX_NAME_LENGTH
];
309 BOOTMON_FS_FILE
*SameFile
;
311 // If the file path start with a \ strip it. The EFI Shell may
312 // insert a \ in front of the file name.
313 if (FileName
[0] == L
'\\') {
317 StrnCpy (TruncFileName
, FileName
, MAX_NAME_LENGTH
- 1);
318 TruncFileName
[MAX_NAME_LENGTH
- 1] = 0;
319 UnicodeStrToAsciiStr (TruncFileName
, AsciiFileName
);
321 if (BootMonGetFileFromAsciiFileName (
325 ) != EFI_NOT_FOUND
) {
326 // A file with that name already exists.
327 return EFI_ACCESS_DENIED
;
329 // OK, change the filename.
330 AsciiStrToUnicodeStr (AsciiFileName
, File
->Info
->FileName
);
336 Set the size of a file.
338 This is a helper function for SetFileInfo().
340 @param[in] Instance A pointer to the description of the volume
342 @param[in] File A pointer to the description of the file.
343 @param[in] NewSize The requested new size for the file.
345 @retval EFI_SUCCESS The size was set.
346 @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request failed.
352 IN BOOTMON_FS_INSTANCE
*Instance
,
353 IN BOOTMON_FS_FILE
*BootMonFsFile
,
359 LIST_ENTRY
*RegionToFlushLink
;
360 LIST_ENTRY
*NextRegionToFlushLink
;
361 BOOTMON_FS_FILE_REGION
*Region
;
362 EFI_FILE_PROTOCOL
*File
;
365 UINT64 StoredPosition
;
367 OldSize
= BootMonFsFile
->Info
->FileSize
;
370 // In case of file truncation, force the regions waiting for writing to
371 // not overflow the new size of the file.
373 if (NewSize
< OldSize
) {
374 for (RegionToFlushLink
= GetFirstNode (&BootMonFsFile
->RegionToFlushLink
);
375 !IsNull (&BootMonFsFile
->RegionToFlushLink
, RegionToFlushLink
);
378 NextRegionToFlushLink
= GetNextNode (&BootMonFsFile
->RegionToFlushLink
, RegionToFlushLink
);
379 Region
= (BOOTMON_FS_FILE_REGION
*)RegionToFlushLink
;
380 if (Region
->Offset
> NewSize
) {
381 RemoveEntryList (RegionToFlushLink
);
382 FreePool (Region
->Buffer
);
385 Region
->Size
= MIN (Region
->Size
, NewSize
- Region
->Offset
);
387 RegionToFlushLink
= NextRegionToFlushLink
;
390 } else if (NewSize
> OldSize
) {
391 // Increasing a file's size is potentially complicated as it may require
392 // moving the image description on media. The simplest way to do it is to
393 // seek past the end of the file (which is valid in UEFI) and perform a
395 File
= &BootMonFsFile
->File
;
398 Status
= File
->GetPosition (File
, &StoredPosition
);
399 if (EFI_ERROR (Status
)) {
402 // Set position at the end of the file
403 Status
= File
->SetPosition (File
, OldSize
);
404 if (EFI_ERROR (Status
)) {
408 BufferSize
= NewSize
- OldSize
;
409 Buffer
= AllocateZeroPool (BufferSize
);
410 if (Buffer
== NULL
) {
411 return EFI_OUT_OF_RESOURCES
;
414 Status
= File
->Write (File
, &BufferSize
, Buffer
);
416 if (EFI_ERROR (Status
)) {
420 // Restore saved position
421 Status
= File
->SetPosition (File
, StoredPosition
);
422 if (EFI_ERROR (Status
)) {
427 BootMonFsFile
->Info
->FileSize
= NewSize
;
433 Set information about a file.
435 @param[in] Instance A pointer to the description of the volume
437 @param[in] File A pointer to the description of the file.
438 @param[in] Info A pointer to the file information to write.
440 @retval EFI_SUCCESS The information was set.
441 @retval EFI_ACCESS_DENIED An attempt is being made to change the
442 EFI_FILE_DIRECTORY Attribute.
443 @retval EFI_ACCESS_DENIED The file was opened in read-only mode and an
444 attempt is being made to modify a field other
446 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file
447 to a file that is already present.
448 @retval EFI_WRITE_PROTECTED An attempt is being made to modify a read-only
450 @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request
457 IN BOOTMON_FS_INSTANCE
*Instance
,
458 IN BOOTMON_FS_FILE
*File
,
459 IN EFI_FILE_INFO
*Info
463 BOOLEAN FileSizeIsDifferent
;
464 BOOLEAN FileNameIsDifferent
;
465 BOOLEAN TimeIsDifferent
;
468 // A directory can not be changed to a file and a file can
469 // not be changed to a directory.
471 if ((Info
->Attribute
& EFI_FILE_DIRECTORY
) !=
472 (File
->Info
->Attribute
& EFI_FILE_DIRECTORY
) ) {
473 return EFI_ACCESS_DENIED
;
476 FileSizeIsDifferent
= (Info
->FileSize
!= File
->Info
->FileSize
);
477 FileNameIsDifferent
= (StrnCmp (
479 File
->Info
->FileName
,
483 // Check if the CreateTime, LastAccess or ModificationTime
484 // have been changed. The file system does not support file
485 // timestamps thus the three times in "File->Info" are
486 // always equal to zero. The following comparison actually
487 // checks if all three times are still equal to 0 or not.
489 TimeIsDifferent
= CompareMem (
491 &File
->Info
->CreateTime
,
492 3 * sizeof (EFI_TIME
)
496 // For a file opened in read-only mode, only the Attribute field can be
497 // modified. The root directory open mode is forced to read-only at opening
498 // thus the following test protects the root directory to be somehow modified.
500 if (File
->OpenMode
== EFI_FILE_MODE_READ
) {
501 if (FileSizeIsDifferent
|| FileNameIsDifferent
|| TimeIsDifferent
) {
502 return EFI_ACCESS_DENIED
;
506 if (TimeIsDifferent
) {
507 return EFI_WRITE_PROTECTED
;
510 if (FileSizeIsDifferent
) {
511 Status
= SetFileSize (Instance
, File
, Info
->FileSize
);
512 if (EFI_ERROR (Status
)) {
518 // Note down in RAM the Attribute field but we can not
519 // ask to store it in flash for the time being.
521 File
->Info
->Attribute
= Info
->Attribute
;
523 if (FileNameIsDifferent
) {
524 Status
= SetFileName (Instance
, File
, Info
->FileName
);
525 if (EFI_ERROR (Status
)) {
536 IN EFI_FILE_PROTOCOL
*This
,
537 IN EFI_GUID
*InformationType
,
538 IN OUT UINTN
*BufferSize
,
543 BOOTMON_FS_FILE
*File
;
544 BOOTMON_FS_INSTANCE
*Instance
;
546 if ((This
== NULL
) ||
547 (InformationType
== NULL
) ||
548 (BufferSize
== NULL
) ||
549 ((Buffer
== NULL
) && (*BufferSize
> 0)) ) {
550 return EFI_INVALID_PARAMETER
;
553 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
554 if (File
->Info
== NULL
) {
555 return EFI_INVALID_PARAMETER
;
557 Instance
= File
->Instance
;
559 // If the instance has not been initialized yet then do it ...
560 if (!Instance
->Initialized
) {
561 Status
= BootMonFsInitialize (Instance
);
563 Status
= EFI_SUCCESS
;
566 if (!EFI_ERROR (Status
)) {
567 if (CompareGuid (InformationType
, &gEfiFileSystemVolumeLabelInfoIdGuid
)
569 Status
= GetFileSystemVolumeLabelInfo (Instance
, BufferSize
, Buffer
);
570 } else if (CompareGuid (InformationType
, &gEfiFileSystemInfoGuid
) != 0) {
571 Status
= GetFilesystemInfo (Instance
, BufferSize
, Buffer
);
572 } else if (CompareGuid (InformationType
, &gEfiFileInfoGuid
) != 0) {
573 Status
= GetFileInfo (Instance
, File
, BufferSize
, Buffer
);
574 } else if (CompareGuid (InformationType
, &gArmBootMonFsFileInfoGuid
) != 0) {
575 Status
= GetBootMonFsFileInfo (Instance
, File
, BufferSize
, Buffer
);
577 Status
= EFI_UNSUPPORTED
;
585 Set information about a file or a volume.
587 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that
588 is the file handle the information is for.
589 @param[in] InformationType The type identifier for the information being set :
590 EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or
591 EFI_FILE_SYSTEM_VOLUME_LABEL_ID
592 @param[in] BufferSize The size, in bytes, of Buffer.
593 @param[in] Buffer A pointer to the data buffer to write. The type of the
594 data inside the buffer is indicated by InformationType.
596 @retval EFI_SUCCESS The information was set.
597 @retval EFI_UNSUPPORTED The InformationType is not known.
598 @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed.
599 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file
600 to a file that is already present.
601 @retval EFI_ACCESS_DENIED An attempt is being made to change the
602 EFI_FILE_DIRECTORY Attribute.
603 @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and
604 the file was opened in read-only mode and an
605 attempt is being made to modify a field other
607 @retval EFI_WRITE_PROTECTED An attempt is being made to modify a read-only
609 @retval EFI_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by
610 the data inside the buffer.
611 @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed.
612 @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.
618 IN EFI_FILE_PROTOCOL
*This
,
619 IN EFI_GUID
*InformationType
,
624 BOOTMON_FS_FILE
*File
;
626 EFI_FILE_SYSTEM_INFO
*SystemInfo
;
628 if ((This
== NULL
) ||
629 (InformationType
== NULL
) ||
631 return EFI_INVALID_PARAMETER
;
634 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
635 if (File
->Info
== NULL
) {
636 return EFI_INVALID_PARAMETER
;
639 if (CompareGuid (InformationType
, &gEfiFileInfoGuid
)) {
641 if (Info
->Size
< (SIZE_OF_EFI_FILE_INFO
+ StrSize (Info
->FileName
))) {
642 return EFI_INVALID_PARAMETER
;
644 if (BufferSize
< Info
->Size
) {
645 return EFI_BAD_BUFFER_SIZE
;
647 return (SetFileInfo (File
->Instance
, File
, Info
));
651 // The only writable field in the other two information types
652 // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the
653 // filesystem volume label. This can be retrieved with GetInfo, but it is
654 // hard-coded into this driver, not stored on media.
657 if (CompareGuid (InformationType
, &gEfiFileSystemInfoGuid
)) {
659 if (SystemInfo
->Size
<
660 (SIZE_OF_EFI_FILE_SYSTEM_INFO
+ StrSize (SystemInfo
->VolumeLabel
))) {
661 return EFI_INVALID_PARAMETER
;
663 if (BufferSize
< SystemInfo
->Size
) {
664 return EFI_BAD_BUFFER_SIZE
;
666 return EFI_WRITE_PROTECTED
;
669 if (CompareGuid (InformationType
, &gEfiFileSystemVolumeLabelInfoIdGuid
)) {
670 return EFI_WRITE_PROTECTED
;
673 return EFI_UNSUPPORTED
;
678 BootMonFsReadDirectory (
679 IN EFI_FILE_PROTOCOL
*This
,
680 IN OUT UINTN
*BufferSize
,
684 BOOTMON_FS_INSTANCE
*Instance
;
685 BOOTMON_FS_FILE
*RootFile
;
686 BOOTMON_FS_FILE
*File
;
693 RootFile
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
694 if (RootFile
== NULL
) {
695 return EFI_INVALID_PARAMETER
;
698 Instance
= RootFile
->Instance
;
699 Status
= BootMonGetFileFromPosition (Instance
, RootFile
->Position
, &File
);
700 if (EFI_ERROR (Status
)) {
706 NameSize
= AsciiStrLen (File
->HwDescription
.Footer
.Filename
) + 1;
707 ResultSize
= SIZE_OF_EFI_FILE_INFO
+ (NameSize
* sizeof (CHAR16
));
708 if (*BufferSize
< ResultSize
) {
709 *BufferSize
= ResultSize
;
710 return EFI_BUFFER_TOO_SMALL
;
713 // Zero out the structure
715 ZeroMem (Info
, ResultSize
);
717 // Fill in the structure
718 Info
->Size
= ResultSize
;
719 Info
->FileSize
= BootMonFsGetImageLength (File
);
720 Info
->PhysicalSize
= BootMonFsGetPhysicalSize (File
);
721 for (Index
= 0; Index
< NameSize
; Index
++) {
722 Info
->FileName
[Index
] = File
->HwDescription
.Footer
.Filename
[Index
];
725 *BufferSize
= ResultSize
;
726 RootFile
->Position
++;
733 BootMonFsFlushDirectory (
734 IN EFI_FILE_PROTOCOL
*This
737 BOOTMON_FS_FILE
*RootFile
;
738 LIST_ENTRY
*ListFiles
;
740 BOOTMON_FS_FILE
*File
;
742 RootFile
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
743 if (RootFile
== NULL
) {
744 return EFI_INVALID_PARAMETER
;
747 ListFiles
= &RootFile
->Link
;
749 if (IsListEmpty (ListFiles
)) {
754 // Flush all the files that need to be flushed
757 // Go through all the list of files to flush them
758 for (Link
= GetFirstNode (ListFiles
);
759 !IsNull (ListFiles
, Link
);
760 Link
= GetNextNode (ListFiles
, Link
)
763 File
= BOOTMON_FS_FILE_FROM_LINK_THIS (Link
);
764 File
->File
.Flush (&File
->File
);