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 CHAR8 AsciiFileName
[MAX_NAME_LENGTH
];
308 BOOTMON_FS_FILE
*SameFile
;
310 // If the file path start with a \ strip it. The EFI Shell may
311 // insert a \ in front of the file name.
312 if (FileName
[0] == L
'\\') {
316 UnicodeStrToAsciiStrS (FileName
, AsciiFileName
, MAX_NAME_LENGTH
);
318 if (BootMonGetFileFromAsciiFileName (
322 ) != EFI_NOT_FOUND
) {
323 // A file with that name already exists.
324 return EFI_ACCESS_DENIED
;
326 // OK, change the filename.
327 AsciiStrToUnicodeStrS (AsciiFileName
, File
->Info
->FileName
,
328 (File
->Info
->Size
- SIZE_OF_EFI_FILE_INFO
) / sizeof (CHAR16
));
334 Set the size of a file.
336 This is a helper function for SetFileInfo().
338 @param[in] Instance A pointer to the description of the volume
340 @param[in] File A pointer to the description of the file.
341 @param[in] NewSize The requested new size for the file.
343 @retval EFI_SUCCESS The size was set.
344 @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request failed.
350 IN BOOTMON_FS_INSTANCE
*Instance
,
351 IN BOOTMON_FS_FILE
*BootMonFsFile
,
357 LIST_ENTRY
*RegionToFlushLink
;
358 LIST_ENTRY
*NextRegionToFlushLink
;
359 BOOTMON_FS_FILE_REGION
*Region
;
360 EFI_FILE_PROTOCOL
*File
;
363 UINT64 StoredPosition
;
365 OldSize
= BootMonFsFile
->Info
->FileSize
;
368 // In case of file truncation, force the regions waiting for writing to
369 // not overflow the new size of the file.
371 if (NewSize
< OldSize
) {
372 for (RegionToFlushLink
= GetFirstNode (&BootMonFsFile
->RegionToFlushLink
);
373 !IsNull (&BootMonFsFile
->RegionToFlushLink
, RegionToFlushLink
);
376 NextRegionToFlushLink
= GetNextNode (&BootMonFsFile
->RegionToFlushLink
, RegionToFlushLink
);
377 Region
= (BOOTMON_FS_FILE_REGION
*)RegionToFlushLink
;
378 if (Region
->Offset
> NewSize
) {
379 RemoveEntryList (RegionToFlushLink
);
380 FreePool (Region
->Buffer
);
383 Region
->Size
= MIN (Region
->Size
, NewSize
- Region
->Offset
);
385 RegionToFlushLink
= NextRegionToFlushLink
;
388 } else if (NewSize
> OldSize
) {
389 // Increasing a file's size is potentially complicated as it may require
390 // moving the image description on media. The simplest way to do it is to
391 // seek past the end of the file (which is valid in UEFI) and perform a
393 File
= &BootMonFsFile
->File
;
396 Status
= File
->GetPosition (File
, &StoredPosition
);
397 if (EFI_ERROR (Status
)) {
400 // Set position at the end of the file
401 Status
= File
->SetPosition (File
, OldSize
);
402 if (EFI_ERROR (Status
)) {
406 BufferSize
= NewSize
- OldSize
;
407 Buffer
= AllocateZeroPool (BufferSize
);
408 if (Buffer
== NULL
) {
409 return EFI_OUT_OF_RESOURCES
;
412 Status
= File
->Write (File
, &BufferSize
, Buffer
);
414 if (EFI_ERROR (Status
)) {
418 // Restore saved position
419 Status
= File
->SetPosition (File
, StoredPosition
);
420 if (EFI_ERROR (Status
)) {
425 BootMonFsFile
->Info
->FileSize
= NewSize
;
431 Set information about a file.
433 @param[in] Instance A pointer to the description of the volume
435 @param[in] File A pointer to the description of the file.
436 @param[in] Info A pointer to the file information to write.
438 @retval EFI_SUCCESS The information was set.
439 @retval EFI_ACCESS_DENIED An attempt is being made to change the
440 EFI_FILE_DIRECTORY Attribute.
441 @retval EFI_ACCESS_DENIED The file was opened in read-only mode and an
442 attempt is being made to modify a field other
444 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file
445 to a file that is already present.
446 @retval EFI_WRITE_PROTECTED An attempt is being made to modify a read-only
448 @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request
455 IN BOOTMON_FS_INSTANCE
*Instance
,
456 IN BOOTMON_FS_FILE
*File
,
457 IN EFI_FILE_INFO
*Info
461 BOOLEAN FileSizeIsDifferent
;
462 BOOLEAN FileNameIsDifferent
;
463 BOOLEAN TimeIsDifferent
;
466 // A directory can not be changed to a file and a file can
467 // not be changed to a directory.
469 if ((Info
->Attribute
& EFI_FILE_DIRECTORY
) !=
470 (File
->Info
->Attribute
& EFI_FILE_DIRECTORY
) ) {
471 return EFI_ACCESS_DENIED
;
474 FileSizeIsDifferent
= (Info
->FileSize
!= File
->Info
->FileSize
);
475 FileNameIsDifferent
= (StrnCmp (
477 File
->Info
->FileName
,
481 // Check if the CreateTime, LastAccess or ModificationTime
482 // have been changed. The file system does not support file
483 // timestamps thus the three times in "File->Info" are
484 // always equal to zero. The following comparison actually
485 // checks if all three times are still equal to 0 or not.
487 TimeIsDifferent
= CompareMem (
489 &File
->Info
->CreateTime
,
490 3 * sizeof (EFI_TIME
)
494 // For a file opened in read-only mode, only the Attribute field can be
495 // modified. The root directory open mode is forced to read-only at opening
496 // thus the following test protects the root directory to be somehow modified.
498 if (File
->OpenMode
== EFI_FILE_MODE_READ
) {
499 if (FileSizeIsDifferent
|| FileNameIsDifferent
|| TimeIsDifferent
) {
500 return EFI_ACCESS_DENIED
;
504 if (TimeIsDifferent
) {
505 return EFI_WRITE_PROTECTED
;
508 if (FileSizeIsDifferent
) {
509 Status
= SetFileSize (Instance
, File
, Info
->FileSize
);
510 if (EFI_ERROR (Status
)) {
516 // Note down in RAM the Attribute field but we can not
517 // ask to store it in flash for the time being.
519 File
->Info
->Attribute
= Info
->Attribute
;
521 if (FileNameIsDifferent
) {
522 Status
= SetFileName (Instance
, File
, Info
->FileName
);
523 if (EFI_ERROR (Status
)) {
534 IN EFI_FILE_PROTOCOL
*This
,
535 IN EFI_GUID
*InformationType
,
536 IN OUT UINTN
*BufferSize
,
541 BOOTMON_FS_FILE
*File
;
542 BOOTMON_FS_INSTANCE
*Instance
;
544 if ((This
== NULL
) ||
545 (InformationType
== NULL
) ||
546 (BufferSize
== NULL
) ||
547 ((Buffer
== NULL
) && (*BufferSize
> 0)) ) {
548 return EFI_INVALID_PARAMETER
;
551 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
552 if (File
->Info
== NULL
) {
553 return EFI_INVALID_PARAMETER
;
555 Instance
= File
->Instance
;
557 // If the instance has not been initialized yet then do it ...
558 if (!Instance
->Initialized
) {
559 Status
= BootMonFsInitialize (Instance
);
561 Status
= EFI_SUCCESS
;
564 if (!EFI_ERROR (Status
)) {
565 if (CompareGuid (InformationType
, &gEfiFileSystemVolumeLabelInfoIdGuid
)
567 Status
= GetFileSystemVolumeLabelInfo (Instance
, BufferSize
, Buffer
);
568 } else if (CompareGuid (InformationType
, &gEfiFileSystemInfoGuid
) != 0) {
569 Status
= GetFilesystemInfo (Instance
, BufferSize
, Buffer
);
570 } else if (CompareGuid (InformationType
, &gEfiFileInfoGuid
) != 0) {
571 Status
= GetFileInfo (Instance
, File
, BufferSize
, Buffer
);
572 } else if (CompareGuid (InformationType
, &gArmBootMonFsFileInfoGuid
) != 0) {
573 Status
= GetBootMonFsFileInfo (Instance
, File
, BufferSize
, Buffer
);
575 Status
= EFI_UNSUPPORTED
;
583 Set information about a file or a volume.
585 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that
586 is the file handle the information is for.
587 @param[in] InformationType The type identifier for the information being set :
588 EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or
589 EFI_FILE_SYSTEM_VOLUME_LABEL_ID
590 @param[in] BufferSize The size, in bytes, of Buffer.
591 @param[in] Buffer A pointer to the data buffer to write. The type of the
592 data inside the buffer is indicated by InformationType.
594 @retval EFI_SUCCESS The information was set.
595 @retval EFI_UNSUPPORTED The InformationType is not known.
596 @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed.
597 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file
598 to a file that is already present.
599 @retval EFI_ACCESS_DENIED An attempt is being made to change the
600 EFI_FILE_DIRECTORY Attribute.
601 @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and
602 the file was opened in read-only mode and an
603 attempt is being made to modify a field other
605 @retval EFI_WRITE_PROTECTED An attempt is being made to modify a read-only
607 @retval EFI_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by
608 the data inside the buffer.
609 @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed.
610 @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.
616 IN EFI_FILE_PROTOCOL
*This
,
617 IN EFI_GUID
*InformationType
,
622 BOOTMON_FS_FILE
*File
;
624 EFI_FILE_SYSTEM_INFO
*SystemInfo
;
626 if ((This
== NULL
) ||
627 (InformationType
== NULL
) ||
629 return EFI_INVALID_PARAMETER
;
632 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
633 if (File
->Info
== NULL
) {
634 return EFI_INVALID_PARAMETER
;
637 if (CompareGuid (InformationType
, &gEfiFileInfoGuid
)) {
639 if (Info
->Size
< (SIZE_OF_EFI_FILE_INFO
+ StrSize (Info
->FileName
))) {
640 return EFI_INVALID_PARAMETER
;
642 if (BufferSize
< Info
->Size
) {
643 return EFI_BAD_BUFFER_SIZE
;
645 return (SetFileInfo (File
->Instance
, File
, Info
));
649 // The only writable field in the other two information types
650 // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the
651 // filesystem volume label. This can be retrieved with GetInfo, but it is
652 // hard-coded into this driver, not stored on media.
655 if (CompareGuid (InformationType
, &gEfiFileSystemInfoGuid
)) {
657 if (SystemInfo
->Size
<
658 (SIZE_OF_EFI_FILE_SYSTEM_INFO
+ StrSize (SystemInfo
->VolumeLabel
))) {
659 return EFI_INVALID_PARAMETER
;
661 if (BufferSize
< SystemInfo
->Size
) {
662 return EFI_BAD_BUFFER_SIZE
;
664 return EFI_WRITE_PROTECTED
;
667 if (CompareGuid (InformationType
, &gEfiFileSystemVolumeLabelInfoIdGuid
)) {
668 return EFI_WRITE_PROTECTED
;
671 return EFI_UNSUPPORTED
;
676 BootMonFsReadDirectory (
677 IN EFI_FILE_PROTOCOL
*This
,
678 IN OUT UINTN
*BufferSize
,
682 BOOTMON_FS_INSTANCE
*Instance
;
683 BOOTMON_FS_FILE
*RootFile
;
684 BOOTMON_FS_FILE
*File
;
691 RootFile
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
692 if (RootFile
== NULL
) {
693 return EFI_INVALID_PARAMETER
;
696 Instance
= RootFile
->Instance
;
697 Status
= BootMonGetFileFromPosition (Instance
, RootFile
->Position
, &File
);
698 if (EFI_ERROR (Status
)) {
704 NameSize
= AsciiStrLen (File
->HwDescription
.Footer
.Filename
) + 1;
705 ResultSize
= SIZE_OF_EFI_FILE_INFO
+ (NameSize
* sizeof (CHAR16
));
706 if (*BufferSize
< ResultSize
) {
707 *BufferSize
= ResultSize
;
708 return EFI_BUFFER_TOO_SMALL
;
711 // Zero out the structure
713 ZeroMem (Info
, ResultSize
);
715 // Fill in the structure
716 Info
->Size
= ResultSize
;
717 Info
->FileSize
= BootMonFsGetImageLength (File
);
718 Info
->PhysicalSize
= BootMonFsGetPhysicalSize (File
);
719 for (Index
= 0; Index
< NameSize
; Index
++) {
720 Info
->FileName
[Index
] = File
->HwDescription
.Footer
.Filename
[Index
];
723 *BufferSize
= ResultSize
;
724 RootFile
->Position
++;
731 BootMonFsFlushDirectory (
732 IN EFI_FILE_PROTOCOL
*This
735 BOOTMON_FS_FILE
*RootFile
;
736 LIST_ENTRY
*ListFiles
;
738 BOOTMON_FS_FILE
*File
;
740 RootFile
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
741 if (RootFile
== NULL
) {
742 return EFI_INVALID_PARAMETER
;
745 ListFiles
= &RootFile
->Link
;
747 if (IsListEmpty (ListFiles
)) {
752 // Flush all the files that need to be flushed
755 // Go through all the list of files to flush them
756 for (Link
= GetFirstNode (ListFiles
);
757 !IsNull (ListFiles
, Link
);
758 Link
= GetNextNode (ListFiles
, Link
)
761 File
= BOOTMON_FS_FILE_FROM_LINK_THIS (Link
);
762 File
->File
.Flush (&File
->File
);