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
) == 0) {
292 // No change to filename.
293 Status
= EFI_SUCCESS
;
294 } else if (!(File
->OpenMode
& EFI_FILE_MODE_WRITE
)) {
295 // You can only change the filename if you open the file for write.
296 Status
= EFI_ACCESS_DENIED
;
297 } else if (BootMonGetFileFromAsciiFileName (
299 File
->HwDescription
.Footer
.Filename
,
300 &SameFile
) != EFI_NOT_FOUND
) {
301 // A file with that name already exists.
302 Status
= EFI_ACCESS_DENIED
;
304 // OK, change the filename.
305 AsciiStrCpy (FileNameAscii
, File
->HwDescription
.Footer
.Filename
);
306 Status
= EFI_SUCCESS
;
309 FreePool (FileNameAscii
);
313 // Set the file's size (NB "size", not "physical size"). If the change amounts
314 // to an increase, simply do a write followed by a flush.
315 // (This is a helper function for SetFileInfo.)
319 IN BOOTMON_FS_INSTANCE
*Instance
,
320 IN BOOTMON_FS_FILE
*BootMonFsFile
,
324 UINT64 StoredPosition
;
326 EFI_FILE_PROTOCOL
*File
;
331 OldSize
= BootMonFsFile
->HwDescription
.Region
[0].Size
;
333 if (OldSize
== NewSize
) {
338 BufferSize
= sizeof (Buffer
);
340 File
= &BootMonFsFile
->File
;
342 if (!(BootMonFsFile
->OpenMode
& EFI_FILE_MODE_WRITE
)) {
343 return EFI_ACCESS_DENIED
;
346 if (NewSize
<= OldSize
) {
349 // Increasing a file's size is potentially complicated as it may require
350 // moving the image description on media. The simplest way to do it is to
351 // seek past the end of the file (which is valid in UEFI) and perform a
355 Status
= File
->GetPosition (File
, &StoredPosition
);
356 if (EFI_ERROR (Status
)) {
360 Status
= File
->SetPosition (File
, NewSize
- 1);
361 if (EFI_ERROR (Status
)) {
364 Status
= File
->Write (File
, &BufferSize
, &Buffer
);
365 if (EFI_ERROR (Status
)) {
369 // Restore saved position
370 Status
= File
->SetPosition (File
, NewSize
- 1);
371 if (EFI_ERROR (Status
)) {
375 Status
= File
->Flush (File
);
376 if (EFI_ERROR (Status
)) {
385 IN BOOTMON_FS_INSTANCE
*Instance
,
386 IN BOOTMON_FS_FILE
*File
,
388 IN EFI_FILE_INFO
*Info
392 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
396 Status
= EFI_SUCCESS
;
397 BlockIo
= Instance
->BlockIo
;
399 // Note that a call to this function on a file opened read-only is only
400 // invalid if it actually changes fields, so we don't immediately fail if the
401 // OpenMode is wrong.
402 // Also note that the only fields supported are filename and size, others are
405 if (File
!= Instance
->RootFile
) {
406 if (!(File
->OpenMode
& EFI_FILE_MODE_WRITE
)) {
407 return EFI_ACCESS_DENIED
;
410 Status
= SetFileName (File
, Info
->FileName
);
411 if (EFI_ERROR (Status
)) {
416 Status
= SetFileSize (Instance
, File
, Info
->FileSize
);
417 if (EFI_ERROR (Status
)) {
422 // Update the last block
424 BlockSize
= BlockIo
->Media
->BlockSize
;
425 DataBuffer
= AllocatePool (BlockSize
);
426 if (DataBuffer
== NULL
) {
427 return EFI_OUT_OF_RESOURCES
;
429 Status
= BlockIo
->ReadBlocks (BlockIo
, Instance
->Media
->MediaId
,
430 File
->HwDescription
.BlockEnd
, BlockSize
, DataBuffer
);
431 if (EFI_ERROR (Status
)) {
432 FreePool (DataBuffer
);
435 CopyMem (DataBuffer
+ BlockSize
- sizeof (File
->HwDescription
), &File
->HwDescription
, sizeof (File
->HwDescription
));
436 Status
= BlockIo
->WriteBlocks (BlockIo
, Instance
->Media
->MediaId
,
437 File
->HwDescription
.BlockEnd
, BlockSize
, DataBuffer
);
438 FreePool (DataBuffer
);
446 IN EFI_FILE_PROTOCOL
*This
,
447 IN EFI_GUID
*InformationType
,
448 IN OUT UINTN
*BufferSize
,
453 BOOTMON_FS_FILE
*File
;
454 BOOTMON_FS_INSTANCE
*Instance
;
456 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
458 return EFI_DEVICE_ERROR
;
461 Instance
= File
->Instance
;
463 // If the instance has not been initialized yet then do it ...
464 if (!Instance
->Initialized
) {
465 Status
= BootMonFsInitialize (Instance
);
467 Status
= EFI_SUCCESS
;
470 if (!EFI_ERROR (Status
)) {
471 if (CompareGuid (InformationType
, &gEfiFileSystemVolumeLabelInfoIdGuid
)
473 Status
= GetFileSystemVolumeLabelInfo (Instance
, BufferSize
, Buffer
);
474 } else if (CompareGuid (InformationType
, &gEfiFileSystemInfoGuid
) != 0) {
475 Status
= GetFilesystemInfo (Instance
, BufferSize
, Buffer
);
476 } else if (CompareGuid (InformationType
, &gEfiFileInfoGuid
) != 0) {
477 Status
= GetFileInfo (Instance
, File
, BufferSize
, Buffer
);
479 Status
= EFI_UNSUPPORTED
;
489 IN EFI_FILE_PROTOCOL
*This
,
490 IN EFI_GUID
*InformationType
,
496 BOOTMON_FS_FILE
*File
;
497 BOOTMON_FS_INSTANCE
*Instance
;
499 File
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
501 return EFI_DEVICE_ERROR
;
504 Instance
= File
->Instance
;
506 if (CompareGuid (InformationType
, &gEfiFileInfoGuid
) != 0) {
507 Status
= SetFileInfo (Instance
, File
, BufferSize
, (EFI_FILE_INFO
*) Buffer
);
509 // The only writable field in the other two information types
510 // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the
511 // filesystem volume label. This can be retrieved with GetInfo, but it is
512 // hard-coded into this driver, not stored on media.
513 Status
= EFI_UNSUPPORTED
;
521 BootMonFsReadDirectory (
522 IN EFI_FILE_PROTOCOL
*This
,
523 IN OUT UINTN
*BufferSize
,
527 BOOTMON_FS_INSTANCE
*Instance
;
528 BOOTMON_FS_FILE
*RootFile
;
529 BOOTMON_FS_FILE
*File
;
536 RootFile
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
537 if (RootFile
== NULL
) {
538 return EFI_INVALID_PARAMETER
;
541 Instance
= RootFile
->Instance
;
542 Status
= BootMonGetFileFromPosition (Instance
, RootFile
->Position
, &File
);
543 if (EFI_ERROR (Status
)) {
549 NameSize
= AsciiStrLen (File
->HwDescription
.Footer
.Filename
) + 1;
550 ResultSize
= SIZE_OF_EFI_FILE_INFO
+ (NameSize
* sizeof (CHAR16
));
551 if (*BufferSize
< ResultSize
) {
552 *BufferSize
= ResultSize
;
553 return EFI_BUFFER_TOO_SMALL
;
556 // Zero out the structure
558 ZeroMem (Info
, ResultSize
);
560 // Fill in the structure
561 Info
->Size
= ResultSize
;
562 Info
->FileSize
= BootMonFsGetImageLength (File
);
563 Info
->PhysicalSize
= BootMonFsGetPhysicalSize (File
);
564 for (Index
= 0; Index
< NameSize
; Index
++) {
565 Info
->FileName
[Index
] = File
->HwDescription
.Footer
.Filename
[Index
];
568 *BufferSize
= ResultSize
;
569 RootFile
->Position
++;
576 BootMonFsFlushDirectory (
577 IN EFI_FILE_PROTOCOL
*This
580 BOOTMON_FS_FILE
*RootFile
;
581 LIST_ENTRY
*ListFiles
;
583 BOOTMON_FS_FILE
*File
;
585 RootFile
= BOOTMON_FS_FILE_FROM_FILE_THIS (This
);
586 if (RootFile
== NULL
) {
587 return EFI_INVALID_PARAMETER
;
590 ListFiles
= &RootFile
->Link
;
592 if (IsListEmpty (ListFiles
)) {
597 // Flush all the files that need to be flushed
600 // Go through all the list of files to flush them
601 for (Link
= GetFirstNode (ListFiles
);
602 !IsNull (ListFiles
, Link
);
603 Link
= GetNextNode (ListFiles
, Link
)
606 File
= BOOTMON_FS_FILE_FROM_LINK_THIS (Link
);
607 File
->File
.Flush (&File
->File
);