]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c
ArmPlatformPkg/BootMonFs: Provide mechanism to get BootMonFS file information
[mirror_edk2.git] / ArmPlatformPkg / FileSystem / BootMonFs / BootMonFsDir.c
1 /** @file
2 *
3 * Copyright (c) 2012-2014, ARM Limited. All rights reserved.
4 *
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
9 *
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.
12 *
13 **/
14
15 #include "BootMonFsInternal.h"
16
17 EFIAPI
18 EFI_STATUS
19 OpenBootMonFsOpenVolume (
20 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
21 OUT EFI_FILE_PROTOCOL **Root
22 )
23 {
24 BOOTMON_FS_INSTANCE *Instance;
25
26 Instance = BOOTMON_FS_FROM_FS_THIS (This);
27 if (Instance == NULL) {
28 return EFI_DEVICE_ERROR;
29 }
30
31 *Root = &Instance->RootFile->File;
32
33 return EFI_SUCCESS;
34 }
35
36 UINT32
37 BootMonFsGetImageLength (
38 IN BOOTMON_FS_FILE *File
39 )
40 {
41 UINT32 Index;
42 UINT32 FileSize;
43 LIST_ENTRY *RegionToFlushLink;
44 BOOTMON_FS_FILE_REGION *Region;
45
46 FileSize = 0;
47
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;
51 }
52
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)
57 )
58 {
59 Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
60 if (Region->Offset + Region->Size > FileSize) {
61 FileSize += Region->Offset + Region->Size;
62 }
63 }
64
65 return FileSize;
66 }
67
68 UINTN
69 BootMonFsGetPhysicalSize (
70 IN BOOTMON_FS_FILE* File
71 )
72 {
73 // Return 0 for files that haven't yet been flushed to media
74 if (File->HwDescription.RegionCount == 0) {
75 return 0;
76 }
77
78 return ((File->HwDescription.BlockEnd - File->HwDescription.BlockStart) + 1 )
79 * File->Instance->Media->BlockSize;
80 }
81
82 EFIAPI
83 EFI_STATUS
84 BootMonFsSetDirPosition (
85 IN EFI_FILE_PROTOCOL *This,
86 IN UINT64 Position
87 )
88 {
89 BOOTMON_FS_FILE *File;
90
91 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
92 if (File == NULL) {
93 return EFI_INVALID_PARAMETER;
94 }
95
96 // UEFI Spec section 12.5:
97 // "The seek request for nonzero is not valid on open directories."
98 if (Position != 0) {
99 return EFI_UNSUPPORTED;
100 }
101 File->Position = Position;
102
103 return EFI_SUCCESS;
104 }
105
106 EFI_STATUS
107 BootMonFsOpenDirectory (
108 OUT EFI_FILE_PROTOCOL **NewHandle,
109 IN CHAR16 *FileName,
110 IN BOOTMON_FS_INSTANCE *Volume
111 )
112 {
113 ASSERT(0);
114
115 return EFI_UNSUPPORTED;
116 }
117 EFI_STATUS
118 GetFileSystemVolumeLabelInfo (
119 IN BOOTMON_FS_INSTANCE *Instance,
120 IN OUT UINTN *BufferSize,
121 OUT VOID *Buffer
122 )
123 {
124 UINTN Size;
125 EFI_FILE_SYSTEM_VOLUME_LABEL *Label;
126 EFI_STATUS Status;
127
128 Label = Buffer;
129
130 // Value returned by StrSize includes null terminator.
131 Size = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL
132 + StrSize (Instance->FsInfo.VolumeLabel);
133
134 if (*BufferSize >= Size) {
135 CopyMem (&Label->VolumeLabel, &Instance->FsInfo.VolumeLabel, Size);
136 Status = EFI_SUCCESS;
137 } else {
138 Status = EFI_BUFFER_TOO_SMALL;
139 }
140 *BufferSize = Size;
141 return Status;
142 }
143
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
149 STATIC
150 UINT64
151 ComputeFreeSpace (
152 IN BOOTMON_FS_INSTANCE *Instance
153 )
154 {
155 LIST_ENTRY *FileLink;
156 UINT64 FileSizeSum;
157 UINT64 MediaSize;
158 UINTN NumFiles;
159 EFI_BLOCK_IO_MEDIA *Media;
160 BOOTMON_FS_FILE *File;
161
162 Media = Instance->BlockIo->Media;
163 MediaSize = Media->BlockSize * (Media->LastBlock + 1);
164
165 NumFiles = 0;
166 FileSizeSum = 0;
167 for (FileLink = GetFirstNode (&Instance->RootFile->Link);
168 !IsNull (&Instance->RootFile->Link, FileLink);
169 FileLink = GetNextNode (&Instance->RootFile->Link, FileLink)
170 )
171 {
172 File = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
173 FileSizeSum += BootMonFsGetImageLength (File);
174
175 NumFiles++;
176 }
177
178 return MediaSize - (FileSizeSum + (Media->BlockSize + NumFiles));
179 }
180
181 EFI_STATUS
182 GetFilesystemInfo (
183 IN BOOTMON_FS_INSTANCE *Instance,
184 IN OUT UINTN *BufferSize,
185 OUT VOID *Buffer
186 )
187 {
188 EFI_STATUS Status;
189
190 if (*BufferSize >= Instance->FsInfo.Size) {
191 Instance->FsInfo.FreeSpace = ComputeFreeSpace (Instance);
192 CopyMem (Buffer, &Instance->FsInfo, Instance->FsInfo.Size);
193 Status = EFI_SUCCESS;
194 } else {
195 Status = EFI_BUFFER_TOO_SMALL;
196 }
197
198 *BufferSize = Instance->FsInfo.Size;
199 return Status;
200 }
201
202 EFI_STATUS
203 GetFileInfo (
204 IN BOOTMON_FS_INSTANCE *Instance,
205 IN BOOTMON_FS_FILE *File,
206 IN OUT UINTN *BufferSize,
207 OUT VOID *Buffer
208 )
209 {
210 EFI_FILE_INFO *Info;
211 UINTN ResultSize;
212 UINTN NameSize;
213 UINTN Index;
214
215 if (File == Instance->RootFile) {
216 NameSize = 0;
217 ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof (CHAR16);
218 } else {
219 NameSize = AsciiStrLen (File->HwDescription.Footer.Filename) + 1;
220 ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16));
221 }
222
223 if (*BufferSize < ResultSize) {
224 *BufferSize = ResultSize;
225 return EFI_BUFFER_TOO_SMALL;
226 }
227
228 Info = Buffer;
229
230 // Zero out the structure
231 ZeroMem (Info, ResultSize);
232
233 // Fill in the structure
234 Info->Size = ResultSize;
235
236 if (File == Instance->RootFile) {
237 Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
238 Info->FileName[0] = L'\0';
239 } else {
240 Info->FileSize = BootMonFsGetImageLength (File);
241 Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
242
243 for (Index = 0; Index < NameSize; Index++) {
244 Info->FileName[Index] = File->HwDescription.Footer.Filename[Index];
245 }
246 }
247
248 *BufferSize = ResultSize;
249
250 return EFI_SUCCESS;
251 }
252
253 STATIC
254 EFI_STATUS
255 GetBootMonFsFileInfo (
256 IN BOOTMON_FS_INSTANCE *Instance,
257 IN BOOTMON_FS_FILE *File,
258 IN OUT UINTN *BufferSize,
259 OUT VOID *Buffer
260 )
261 {
262 EFI_STATUS Status;
263 BOOTMON_FS_FILE_INFO *Info;
264 UINTN ResultSize;
265 UINTN Index;
266
267 if (File == Instance->RootFile) {
268 Status = EFI_UNSUPPORTED;
269 } else {
270 ResultSize = SIZE_OF_BOOTMON_FS_FILE_INFO;
271
272 if (*BufferSize < ResultSize) {
273 *BufferSize = ResultSize;
274 Status = EFI_BUFFER_TOO_SMALL;
275 } else {
276 Info = Buffer;
277
278 // Zero out the structure
279 ZeroMem (Info, ResultSize);
280
281 // Fill in the structure
282 Info->Size = ResultSize;
283
284 Info->EntryPoint = File->HwDescription.EntryPoint;
285 Info->RegionCount = File->HwDescription.RegionCount;
286 for (Index = 0; Index < File->HwDescription.RegionCount; Index++) {
287 Info->Region[Index].LoadAddress = File->HwDescription.Region[Index].LoadAddress;
288 Info->Region[Index].Size = File->HwDescription.Region[Index].Size;
289 Info->Region[Index].Offset = File->HwDescription.Region[Index].Offset;
290 Info->Region[Index].Checksum = File->HwDescription.Region[Index].Checksum;
291 }
292 *BufferSize = ResultSize;
293 Status = EFI_SUCCESS;
294 }
295 }
296
297 return Status;
298 }
299
300 STATIC
301 EFI_STATUS
302 SetFileName (
303 IN BOOTMON_FS_FILE *File,
304 IN CHAR16 *FileNameUnicode
305 )
306 {
307 CHAR8 *FileNameAscii;
308 UINT16 SavedChar;
309 UINTN FileNameSize;
310 BOOTMON_FS_FILE *SameFile;
311 EFI_STATUS Status;
312
313 // EFI Shell inserts '\' in front of the filename that must be stripped
314 if (FileNameUnicode[0] == L'\\') {
315 FileNameUnicode++;
316 }
317 //
318 // Convert Unicode into Ascii
319 //
320 SavedChar = L'\0';
321 FileNameSize = StrLen (FileNameUnicode) + 1;
322 FileNameAscii = AllocatePool (FileNameSize * sizeof (CHAR8));
323 if (FileNameAscii == NULL) {
324 return EFI_OUT_OF_RESOURCES;
325 }
326 // If Unicode string is too long then truncate it.
327 if (FileNameSize > MAX_NAME_LENGTH) {
328 SavedChar = FileNameUnicode[MAX_NAME_LENGTH - 1];
329 FileNameUnicode[MAX_NAME_LENGTH - 1] = L'\0';
330 }
331 UnicodeStrToAsciiStr (FileNameUnicode, FileNameAscii);
332 // If the unicode string was truncated then restore its original content.
333 if (SavedChar != L'\0') {
334 FileNameUnicode[MAX_NAME_LENGTH - 1] = SavedChar;
335 }
336
337 // If we're changing the file name
338 if (AsciiStrCmp (FileNameAscii, File->HwDescription.Footer.Filename) == 0) {
339 // No change to filename.
340 Status = EFI_SUCCESS;
341 } else if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) {
342 // You can only change the filename if you open the file for write.
343 Status = EFI_ACCESS_DENIED;
344 } else if (BootMonGetFileFromAsciiFileName (
345 File->Instance,
346 File->HwDescription.Footer.Filename,
347 &SameFile) != EFI_NOT_FOUND) {
348 // A file with that name already exists.
349 Status = EFI_ACCESS_DENIED;
350 } else {
351 // OK, change the filename.
352 AsciiStrCpy (FileNameAscii, File->HwDescription.Footer.Filename);
353 Status = EFI_SUCCESS;
354 }
355
356 FreePool (FileNameAscii);
357 return Status;
358 }
359
360 // Set the file's size (NB "size", not "physical size"). If the change amounts
361 // to an increase, simply do a write followed by a flush.
362 // (This is a helper function for SetFileInfo.)
363 STATIC
364 EFI_STATUS
365 SetFileSize (
366 IN BOOTMON_FS_INSTANCE *Instance,
367 IN BOOTMON_FS_FILE *BootMonFsFile,
368 IN UINTN NewSize
369 )
370 {
371 UINT64 StoredPosition;
372 EFI_STATUS Status;
373 EFI_FILE_PROTOCOL *File;
374 CHAR8 Buffer;
375 UINTN BufferSize;
376 UINT32 OldSize;
377
378 OldSize = BootMonFsFile->HwDescription.Region[0].Size;
379
380 if (OldSize == NewSize) {
381 return EFI_SUCCESS;
382 }
383
384 Buffer = 0;
385 BufferSize = sizeof (Buffer);
386
387 File = &BootMonFsFile->File;
388
389 if (!(BootMonFsFile->OpenMode & EFI_FILE_MODE_WRITE)) {
390 return EFI_ACCESS_DENIED;
391 }
392
393 if (NewSize <= OldSize) {
394 OldSize = NewSize;
395 } else {
396 // Increasing a file's size is potentially complicated as it may require
397 // moving the image description on media. The simplest way to do it is to
398 // seek past the end of the file (which is valid in UEFI) and perform a
399 // Write.
400
401 // Save position
402 Status = File->GetPosition (File, &StoredPosition);
403 if (EFI_ERROR (Status)) {
404 return Status;
405 }
406
407 Status = File->SetPosition (File, NewSize - 1);
408 if (EFI_ERROR (Status)) {
409 return Status;
410 }
411 Status = File->Write (File, &BufferSize, &Buffer);
412 if (EFI_ERROR (Status)) {
413 return Status;
414 }
415
416 // Restore saved position
417 Status = File->SetPosition (File, NewSize - 1);
418 if (EFI_ERROR (Status)) {
419 return Status;
420 }
421
422 Status = File->Flush (File);
423 if (EFI_ERROR (Status)) {
424 return Status;
425 }
426 }
427 return EFI_SUCCESS;
428 }
429
430 EFI_STATUS
431 SetFileInfo (
432 IN BOOTMON_FS_INSTANCE *Instance,
433 IN BOOTMON_FS_FILE *File,
434 IN UINTN BufferSize,
435 IN EFI_FILE_INFO *Info
436 )
437 {
438 EFI_STATUS Status;
439
440 Status = EFI_SUCCESS;
441
442 // Note that a call to this function on a file opened read-only is only
443 // invalid if it actually changes fields, so we don't immediately fail if the
444 // OpenMode is wrong.
445 // Also note that the only fields supported are filename and size, others are
446 // ignored.
447
448 if (File != Instance->RootFile) {
449 if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) {
450 return EFI_ACCESS_DENIED;
451 }
452
453 Status = SetFileName (File, Info->FileName);
454 if (EFI_ERROR (Status)) {
455 return Status;
456 }
457
458 // Update file size
459 Status = SetFileSize (Instance, File, Info->FileSize);
460 if (EFI_ERROR (Status)) {
461 return Status;
462 }
463 }
464 return Status;
465 }
466
467 EFIAPI
468 EFI_STATUS
469 BootMonFsGetInfo (
470 IN EFI_FILE_PROTOCOL *This,
471 IN EFI_GUID *InformationType,
472 IN OUT UINTN *BufferSize,
473 OUT VOID *Buffer
474 )
475 {
476 EFI_STATUS Status;
477 BOOTMON_FS_FILE *File;
478 BOOTMON_FS_INSTANCE *Instance;
479
480 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
481 if (File == NULL) {
482 return EFI_DEVICE_ERROR;
483 }
484
485 Instance = File->Instance;
486
487 // If the instance has not been initialized yet then do it ...
488 if (!Instance->Initialized) {
489 Status = BootMonFsInitialize (Instance);
490 } else {
491 Status = EFI_SUCCESS;
492 }
493
494 if (!EFI_ERROR (Status)) {
495 if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)
496 != 0) {
497 Status = GetFileSystemVolumeLabelInfo (Instance, BufferSize, Buffer);
498 } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {
499 Status = GetFilesystemInfo (Instance, BufferSize, Buffer);
500 } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
501 Status = GetFileInfo (Instance, File, BufferSize, Buffer);
502 } else if (CompareGuid (InformationType, &gArmBootMonFsFileInfoGuid) != 0) {
503 Status = GetBootMonFsFileInfo (Instance, File, BufferSize, Buffer);
504 } else {
505 Status = EFI_UNSUPPORTED;
506 }
507 }
508
509 return Status;
510 }
511
512 EFIAPI
513 EFI_STATUS
514 BootMonFsSetInfo (
515 IN EFI_FILE_PROTOCOL *This,
516 IN EFI_GUID *InformationType,
517 IN UINTN BufferSize,
518 IN VOID *Buffer
519 )
520 {
521 EFI_STATUS Status;
522 BOOTMON_FS_FILE *File;
523 BOOTMON_FS_INSTANCE *Instance;
524
525 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
526 if (File == NULL) {
527 return EFI_DEVICE_ERROR;
528 }
529
530 Instance = File->Instance;
531
532 if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
533 Status = SetFileInfo (Instance, File, BufferSize, (EFI_FILE_INFO *) Buffer);
534 } else {
535 // The only writable field in the other two information types
536 // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the
537 // filesystem volume label. This can be retrieved with GetInfo, but it is
538 // hard-coded into this driver, not stored on media.
539 Status = EFI_UNSUPPORTED;
540 }
541
542 return Status;
543 }
544
545 EFIAPI
546 EFI_STATUS
547 BootMonFsReadDirectory (
548 IN EFI_FILE_PROTOCOL *This,
549 IN OUT UINTN *BufferSize,
550 OUT VOID *Buffer
551 )
552 {
553 BOOTMON_FS_INSTANCE *Instance;
554 BOOTMON_FS_FILE *RootFile;
555 BOOTMON_FS_FILE *File;
556 EFI_FILE_INFO *Info;
557 UINTN NameSize;
558 UINTN ResultSize;
559 EFI_STATUS Status;
560 UINTN Index;
561
562 RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
563 if (RootFile == NULL) {
564 return EFI_INVALID_PARAMETER;
565 }
566
567 Instance = RootFile->Instance;
568 Status = BootMonGetFileFromPosition (Instance, RootFile->Position, &File);
569 if (EFI_ERROR (Status)) {
570 // No more file
571 *BufferSize = 0;
572 return EFI_SUCCESS;
573 }
574
575 NameSize = AsciiStrLen (File->HwDescription.Footer.Filename) + 1;
576 ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16));
577 if (*BufferSize < ResultSize) {
578 *BufferSize = ResultSize;
579 return EFI_BUFFER_TOO_SMALL;
580 }
581
582 // Zero out the structure
583 Info = Buffer;
584 ZeroMem (Info, ResultSize);
585
586 // Fill in the structure
587 Info->Size = ResultSize;
588 Info->FileSize = BootMonFsGetImageLength (File);
589 Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
590 for (Index = 0; Index < NameSize; Index++) {
591 Info->FileName[Index] = File->HwDescription.Footer.Filename[Index];
592 }
593
594 *BufferSize = ResultSize;
595 RootFile->Position++;
596
597 return EFI_SUCCESS;
598 }
599
600 EFIAPI
601 EFI_STATUS
602 BootMonFsFlushDirectory (
603 IN EFI_FILE_PROTOCOL *This
604 )
605 {
606 BOOTMON_FS_FILE *RootFile;
607 LIST_ENTRY *ListFiles;
608 LIST_ENTRY *Link;
609 BOOTMON_FS_FILE *File;
610
611 RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
612 if (RootFile == NULL) {
613 return EFI_INVALID_PARAMETER;
614 }
615
616 ListFiles = &RootFile->Link;
617
618 if (IsListEmpty (ListFiles)) {
619 return EFI_SUCCESS;
620 }
621
622 //
623 // Flush all the files that need to be flushed
624 //
625
626 // Go through all the list of files to flush them
627 for (Link = GetFirstNode (ListFiles);
628 !IsNull (ListFiles, Link);
629 Link = GetNextNode (ListFiles, Link)
630 )
631 {
632 File = BOOTMON_FS_FILE_FROM_LINK_THIS (Link);
633 File->File.Flush (&File->File);
634 }
635
636 return EFI_SUCCESS;
637 }