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 *Root
= &Instance
->RootFile
->File
;
37 BootMonFsGetImageLength (
38 IN BOOTMON_FS_FILE
*File
43 LIST_ENTRY
*RegionToFlushLink
;
44 BOOTMON_FS_FILE_REGION
*Region
;
48 // Look at all Flash areas to determine file size
49 for (Index
= 0; Index
< HW_IMAGE_DESCRIPTION_REGION_MAX
; Index
++) {
50 FileSize
+= File
->HwDescription
.Region
[Index
].Size
;
53 // Add the regions that have not been flushed yet
54 for (RegionToFlushLink
= GetFirstNode (&File
->RegionToFlushLink
);
55 !IsNull (&File
->RegionToFlushLink
, RegionToFlushLink
);
56 RegionToFlushLink
= GetNextNode (&File
->RegionToFlushLink
, RegionToFlushLink
)
59 Region
= (BOOTMON_FS_FILE_REGION
*)RegionToFlushLink
;
60 if (Region
->Offset
+ Region
->Size
> FileSize
) {
61 FileSize
+= Region
->Offset
+ Region
->Size
;
69 BootMonFsGetPhysicalSize (
70 IN BOOTMON_FS_FILE
* File
73 // Return 0 for files that haven't yet been flushed to media
74 if (File
->HwDescription
.RegionCount
== 0) {
78 return ((File
->HwDescription
.BlockEnd
- File
->HwDescription
.BlockStart
) + 1 )
79 * File
->Instance
->Media
->BlockSize
;
84 BootMonFsSetDirPosition (
85 IN EFI_FILE_PROTOCOL
*This
,
89 BOOTMON_FS_FILE
*File
;
91 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
93 return EFI_INVALID_PARAMETER
;
96 // UEFI Spec section 12.5:
97 // "The seek request for nonzero is not valid on open directories."
99 return EFI_UNSUPPORTED
;
101 File
->Position
= Position
;
107 BootMonFsOpenDirectory (
108 OUT EFI_FILE_PROTOCOL
**NewHandle
,
110 IN BOOTMON_FS_INSTANCE
*Volume
115 return EFI_UNSUPPORTED
;
118 GetFileSystemVolumeLabelInfo (
119 IN BOOTMON_FS_INSTANCE
*Instance
,
120 IN OUT UINTN
*BufferSize
,
125 EFI_FILE_SYSTEM_VOLUME_LABEL
*Label
;
130 // Value returned by StrSize includes null terminator.
131 Size
= SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL
132 + StrSize (Instance
->FsInfo
.VolumeLabel
);
134 if (*BufferSize
>= Size
) {
135 CopyMem (&Label
->VolumeLabel
, &Instance
->FsInfo
.VolumeLabel
, Size
);
136 Status
= EFI_SUCCESS
;
138 Status
= EFI_BUFFER_TOO_SMALL
;
144 // Helper function that calculates a rough "free space" by:
145 // - Taking the media size
146 // - Subtracting the sum of all file sizes
147 // - Subtracting the block size times the number of files
148 // (To account for the blocks containing the HW_IMAGE_INFO
152 IN BOOTMON_FS_INSTANCE
*Instance
155 LIST_ENTRY
*FileLink
;
159 EFI_BLOCK_IO_MEDIA
*Media
;
160 BOOTMON_FS_FILE
*File
;
162 Media
= Instance
->BlockIo
->Media
;
163 MediaSize
= Media
->BlockSize
* (Media
->LastBlock
+ 1);
167 for (FileLink
= GetFirstNode (&Instance
->RootFile
->Link
);
168 !IsNull (&Instance
->RootFile
->Link
, FileLink
);
169 FileLink
= GetNextNode (&Instance
->RootFile
->Link
, FileLink
)
172 File
= BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink
);
173 FileSizeSum
+= BootMonFsGetImageLength (File
);
178 return MediaSize
- (FileSizeSum
+ (Media
->BlockSize
+ NumFiles
));
183 IN BOOTMON_FS_INSTANCE
*Instance
,
184 IN OUT UINTN
*BufferSize
,
190 if (*BufferSize
>= Instance
->FsInfo
.Size
) {
191 Instance
->FsInfo
.FreeSpace
= ComputeFreeSpace (Instance
);
192 CopyMem (Buffer
, &Instance
->FsInfo
, Instance
->FsInfo
.Size
);
193 Status
= EFI_SUCCESS
;
195 Status
= EFI_BUFFER_TOO_SMALL
;
198 *BufferSize
= Instance
->FsInfo
.Size
;
204 IN BOOTMON_FS_INSTANCE
*Instance
,
205 IN BOOTMON_FS_FILE
*File
,
206 IN OUT UINTN
*BufferSize
,
215 if (File
== Instance
->RootFile
) {
217 ResultSize
= SIZE_OF_EFI_FILE_INFO
+ sizeof (CHAR16
);
219 NameSize
= AsciiStrLen (File
->HwDescription
.Footer
.Filename
) + 1;
220 ResultSize
= SIZE_OF_EFI_FILE_INFO
+ (NameSize
* sizeof (CHAR16
));
223 if (*BufferSize
< ResultSize
) {
224 *BufferSize
= ResultSize
;
225 return EFI_BUFFER_TOO_SMALL
;
230 // Zero out the structure
231 ZeroMem (Info
, ResultSize
);
233 // Fill in the structure
234 Info
->Size
= ResultSize
;
236 if (File
== Instance
->RootFile
) {
237 Info
->Attribute
= EFI_FILE_READ_ONLY
| EFI_FILE_DIRECTORY
;
238 Info
->FileName
[0] = L
'\0';
240 Info
->FileSize
= BootMonFsGetImageLength (File
);
241 Info
->PhysicalSize
= BootMonFsGetPhysicalSize (File
);
243 for (Index
= 0; Index
< NameSize
; Index
++) {
244 Info
->FileName
[Index
] = File
->HwDescription
.Footer
.Filename
[Index
];
248 *BufferSize
= ResultSize
;
256 IN BOOTMON_FS_FILE
*File
,
257 IN CHAR16
*FileNameUnicode
260 CHAR8
*FileNameAscii
;
263 BOOTMON_FS_FILE
*SameFile
;
266 // EFI Shell inserts '\' in front of the filename that must be stripped
267 if (FileNameUnicode
[0] == L
'\\') {
271 // Convert Unicode into Ascii
274 FileNameSize
= StrLen (FileNameUnicode
) + 1;
275 FileNameAscii
= AllocatePool (FileNameSize
* sizeof (CHAR8
));
276 if (FileNameAscii
== NULL
) {
277 return EFI_OUT_OF_RESOURCES
;
279 // If Unicode string is too long then truncate it.
280 if (FileNameSize
> MAX_NAME_LENGTH
) {
281 SavedChar
= FileNameUnicode
[MAX_NAME_LENGTH
- 1];
282 FileNameUnicode
[MAX_NAME_LENGTH
- 1] = L
'\0';
284 UnicodeStrToAsciiStr (FileNameUnicode
, FileNameAscii
);
285 // If the unicode string was truncated then restore its original content.
286 if (SavedChar
!= L
'\0') {
287 FileNameUnicode
[MAX_NAME_LENGTH
- 1] = SavedChar
;
290 // If we're changing the file name
291 if (AsciiStrCmp (FileNameAscii
, File
->HwDescription
.Footer
.Filename
)) {
292 // Check a file with that filename doesn't already exist
293 if (BootMonGetFileFromAsciiFileName (
295 File
->HwDescription
.Footer
.Filename
,
296 &SameFile
) != EFI_NOT_FOUND
) {
297 Status
= EFI_ACCESS_DENIED
;
299 AsciiStrCpy (FileNameAscii
, File
->HwDescription
.Footer
.Filename
);
300 Status
= EFI_SUCCESS
;
303 // No change to filename
304 Status
= EFI_SUCCESS
;
307 FreePool (FileNameAscii
);
311 // Set the file's size (NB "size", not "physical size"). If the change amounts
312 // to an increase, simply do a write followed by a flush.
313 // (This is a helper function for SetFileInfo.)
317 IN BOOTMON_FS_INSTANCE
*Instance
,
318 IN BOOTMON_FS_FILE
*BootMonFsFile
,
322 UINT64 StoredPosition
;
324 EFI_FILE_PROTOCOL
*File
;
329 BufferSize
= sizeof (Buffer
);
331 File
= &BootMonFsFile
->File
;
333 if (!(BootMonFsFile
->OpenMode
& EFI_FILE_MODE_WRITE
)) {
334 return EFI_ACCESS_DENIED
;
337 if (Size
<= BootMonFsFile
->HwDescription
.Region
[0].Size
) {
338 BootMonFsFile
->HwDescription
.Region
[0].Size
= Size
;
340 // Increasing a file's size is potentially complicated as it may require
341 // moving the image description on media. The simplest way to do it is to
342 // seek past the end of the file (which is valid in UEFI) and perform a
346 Status
= File
->GetPosition (File
, &StoredPosition
);
347 if (EFI_ERROR (Status
)) {
351 Status
= File
->SetPosition (File
, Size
- 1);
352 if (EFI_ERROR (Status
)) {
355 Status
= File
->Write (File
, &BufferSize
, &Buffer
);
356 if (EFI_ERROR (Status
)) {
360 // Restore saved position
361 Status
= File
->SetPosition (File
, Size
- 1);
362 if (EFI_ERROR (Status
)) {
366 Status
= File
->Flush (File
);
367 if (EFI_ERROR (Status
)) {
376 IN BOOTMON_FS_INSTANCE
*Instance
,
377 IN BOOTMON_FS_FILE
*File
,
379 IN EFI_FILE_INFO
*Info
383 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
387 Status
= EFI_SUCCESS
;
388 BlockIo
= Instance
->BlockIo
;
390 // Note that a call to this function on a file opened read-only is only
391 // invalid if it actually changes fields, so we don't immediately fail if the
392 // OpenMode is wrong.
393 // Also note that the only fields supported are filename and size, others are
396 if (File
!= Instance
->RootFile
) {
397 if (!(File
->OpenMode
& EFI_FILE_MODE_WRITE
)) {
398 return EFI_ACCESS_DENIED
;
401 SetFileName (File
, Info
->FileName
);
402 if (EFI_ERROR (Status
)) {
407 Status
= SetFileSize (Instance
, File
, Info
->FileSize
);
408 if (EFI_ERROR (Status
)) {
413 // Update the last block
415 BlockSize
= BlockIo
->Media
->BlockSize
;
416 DataBuffer
= AllocatePool (BlockSize
);
417 if (DataBuffer
== NULL
) {
418 return EFI_OUT_OF_RESOURCES
;
420 Status
= BlockIo
->ReadBlocks (BlockIo
, Instance
->Media
->MediaId
,
421 File
->HwDescription
.BlockEnd
, BlockSize
, DataBuffer
);
422 if (EFI_ERROR (Status
)) {
423 FreePool (DataBuffer
);
426 CopyMem (DataBuffer
+ BlockSize
- sizeof (File
->HwDescription
), &File
->HwDescription
, sizeof (File
->HwDescription
));
427 Status
= BlockIo
->WriteBlocks (BlockIo
, Instance
->Media
->MediaId
,
428 File
->HwDescription
.BlockEnd
, BlockSize
, DataBuffer
);
429 FreePool (DataBuffer
);
437 IN EFI_FILE_PROTOCOL
*This
,
438 IN EFI_GUID
*InformationType
,
439 IN OUT UINTN
*BufferSize
,
444 BOOTMON_FS_FILE
*File
;
445 BOOTMON_FS_INSTANCE
*Instance
;
447 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
449 return EFI_DEVICE_ERROR
;
452 Instance
= File
->Instance
;
454 // If the instance has not been initialized yet then do it ...
455 if (!Instance
->Initialized
) {
456 Status
= BootMonFsInitialize (Instance
);
458 Status
= EFI_SUCCESS
;
461 if (!EFI_ERROR (Status
)) {
462 if (CompareGuid (InformationType
, &gEfiFileSystemVolumeLabelInfoIdGuid
)
464 Status
= GetFileSystemVolumeLabelInfo (Instance
, BufferSize
, Buffer
);
465 } else if (CompareGuid (InformationType
, &gEfiFileSystemInfoGuid
) != 0) {
466 Status
= GetFilesystemInfo (Instance
, BufferSize
, Buffer
);
467 } else if (CompareGuid (InformationType
, &gEfiFileInfoGuid
) != 0) {
468 Status
= GetFileInfo (Instance
, File
, BufferSize
, Buffer
);
470 Status
= EFI_UNSUPPORTED
;
480 IN EFI_FILE_PROTOCOL
*This
,
481 IN EFI_GUID
*InformationType
,
487 BOOTMON_FS_FILE
*File
;
488 BOOTMON_FS_INSTANCE
*Instance
;
490 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
492 return EFI_DEVICE_ERROR
;
495 Instance
= File
->Instance
;
497 if (CompareGuid (InformationType
, &gEfiFileInfoGuid
) != 0) {
498 Status
= SetFileInfo (Instance
, File
, BufferSize
, (EFI_FILE_INFO
*) Buffer
);
500 // The only writable field in the other two information types
501 // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the
502 // filesystem volume label. This can be retrieved with GetInfo, but it is
503 // hard-coded into this driver, not stored on media.
504 Status
= EFI_UNSUPPORTED
;
512 BootMonFsReadDirectory (
513 IN EFI_FILE_PROTOCOL
*This
,
514 IN OUT UINTN
*BufferSize
,
518 BOOTMON_FS_INSTANCE
*Instance
;
519 BOOTMON_FS_FILE
*RootFile
;
520 BOOTMON_FS_FILE
*File
;
527 RootFile
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
528 if (RootFile
== NULL
) {
529 return EFI_INVALID_PARAMETER
;
532 Instance
= RootFile
->Instance
;
533 Status
= BootMonGetFileFromPosition (Instance
, RootFile
->Position
, &File
);
534 if (EFI_ERROR (Status
)) {
540 NameSize
= AsciiStrLen (File
->HwDescription
.Footer
.Filename
) + 1;
541 ResultSize
= SIZE_OF_EFI_FILE_INFO
+ (NameSize
* sizeof (CHAR16
));
542 if (*BufferSize
< ResultSize
) {
543 *BufferSize
= ResultSize
;
544 return EFI_BUFFER_TOO_SMALL
;
547 // Zero out the structure
549 ZeroMem (Info
, ResultSize
);
551 // Fill in the structure
552 Info
->Size
= ResultSize
;
553 Info
->FileSize
= BootMonFsGetImageLength (File
);
554 Info
->PhysicalSize
= BootMonFsGetPhysicalSize (File
);
555 for (Index
= 0; Index
< NameSize
; Index
++) {
556 Info
->FileName
[Index
] = File
->HwDescription
.Footer
.Filename
[Index
];
559 *BufferSize
= ResultSize
;
560 RootFile
->Position
++;
567 BootMonFsFlushDirectory (
568 IN EFI_FILE_PROTOCOL
*This
571 BOOTMON_FS_FILE
*RootFile
;
572 LIST_ENTRY
*ListFiles
;
574 BOOTMON_FS_FILE
*File
;
576 RootFile
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
577 if (RootFile
== NULL
) {
578 return EFI_INVALID_PARAMETER
;
581 ListFiles
= &RootFile
->Link
;
583 if (IsListEmpty (ListFiles
)) {
588 // Flush all the files that need to be flushed
591 // Go through all the list of files to flush them
592 for (Link
= GetFirstNode (ListFiles
);
593 !IsNull (ListFiles
, Link
);
594 Link
= GetNextNode (ListFiles
, Link
)
597 File
= BOOTMON_FS_FILE_FROM_LINK_THIS (Link
);
598 File
->File
.Flush (&File
->File
);