| /*++ | |
| Caution: This file is used for Duet platform only, do not use them in real platform. | |
| All variable code, variable metadata, and variable data used by Duet platform are on | |
| disk. They can be changed by user. BIOS is not able to protoect those. | |
| Duet trusts all meta data from disk. If variable code, variable metadata and variable | |
| data is modified in inproper way, the behavior is undefined. | |
| Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| Module Name: | |
| FileStorage.c | |
| Abstract: | |
| handles variable store/reads on file | |
| Revision History | |
| --*/ | |
| #include "FSVariable.h" | |
| VOID *mSFSRegistration; | |
| // | |
| // Prototypes | |
| // | |
| VOID | |
| EFIAPI | |
| OnVirtualAddressChangeFs ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ); | |
| EFI_STATUS | |
| EFIAPI | |
| FileEraseStore( | |
| IN VARIABLE_STORAGE *This | |
| ); | |
| EFI_STATUS | |
| EFIAPI | |
| FileWriteStore ( | |
| IN VARIABLE_STORAGE *This, | |
| IN UINTN Offset, | |
| IN UINTN BufferSize, | |
| IN VOID *Buffer | |
| ); | |
| EFI_STATUS | |
| OpenStore ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *Device, | |
| IN CHAR16 *FilePathName, | |
| IN UINT64 OpenMode, | |
| OUT EFI_FILE_PROTOCOL **File | |
| ); | |
| // | |
| // Implementation below: | |
| // | |
| VOID | |
| FileClose ( | |
| IN EFI_FILE_PROTOCOL *File | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = File->Flush (File); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = File->Close (File); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| EFI_STATUS | |
| CheckStore ( | |
| IN EFI_HANDLE SimpleFileSystemHandle, | |
| IN UINT32 VolumeId, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **Device | |
| ) | |
| { | |
| #define BLOCK_SIZE 0x200 | |
| #define FAT16_VOLUME_ID_OFFSET 39 | |
| #define FAT32_VOLUME_ID_OFFSET 67 | |
| EFI_STATUS Status; | |
| EFI_BLOCK_IO_PROTOCOL *BlkIo; | |
| UINT8 BootSector[BLOCK_SIZE]; | |
| *Device = NULL; | |
| Status = gBS->HandleProtocol ( | |
| SimpleFileSystemHandle, | |
| &gEfiBlockIoProtocolGuid, // BlockIo should be supported if it supports SimpleFileSystem | |
| (VOID*)&BlkIo | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto ErrHandle; | |
| } | |
| if (!BlkIo->Media->MediaPresent) { | |
| DEBUG ((EFI_D_ERROR, "FileStorage: Media not present!\n")); | |
| Status = EFI_NO_MEDIA; | |
| goto ErrHandle; | |
| } | |
| if (BlkIo->Media->ReadOnly) { | |
| DEBUG ((EFI_D_ERROR, "FileStorage: Media is read-only!\n")); | |
| Status = EFI_ACCESS_DENIED; | |
| goto ErrHandle; | |
| } | |
| Status = BlkIo->ReadBlocks( | |
| BlkIo, | |
| BlkIo->Media->MediaId, | |
| 0, | |
| BLOCK_SIZE, | |
| BootSector | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if ((*(UINT32 *) &BootSector[FAT16_VOLUME_ID_OFFSET] != VolumeId) && | |
| (*(UINT32 *) &BootSector[FAT32_VOLUME_ID_OFFSET] != VolumeId) | |
| ) { | |
| Status = EFI_NOT_FOUND; | |
| goto ErrHandle; | |
| } | |
| *Device = DuplicateDevicePath (DevicePathFromHandle (SimpleFileSystemHandle)); | |
| ASSERT (*Device != NULL); | |
| ErrHandle: | |
| return Status; | |
| } | |
| EFI_STATUS | |
| CheckStoreExists ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *Device | |
| ) | |
| { | |
| EFI_HANDLE Handle; | |
| EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; | |
| EFI_STATUS Status; | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| &Device, | |
| &Handle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gBS->HandleProtocol ( | |
| Handle, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| (VOID **) &Volume | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // this routine is still running in BS period, no limitation | |
| // call FileInitStorage(), which load variable content file to memory | |
| // read the store_header, init store_header if it has not been inited (read sth. about format/heathy) | |
| // reclaim space using scratch memory | |
| VOID | |
| EFIAPI | |
| OnSimpleFileSystemInstall ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN HandleSize; | |
| EFI_HANDLE Handle; | |
| EFI_DEVICE_PATH_PROTOCOL *Device; | |
| VS_DEV *Dev; | |
| EFI_FILE_PROTOCOL *File; | |
| UINTN NumBytes; | |
| Dev = (VS_DEV *) Context; | |
| if (VAR_FILE_DEVICEPATH (Dev) != NULL && | |
| !EFI_ERROR (CheckStoreExists (VAR_FILE_DEVICEPATH (Dev))) | |
| ) { | |
| DEBUG ((EFI_D_ERROR, "FileStorage: Already mapped!\n")); | |
| return ; | |
| } | |
| while (TRUE) { | |
| HandleSize = sizeof (EFI_HANDLE); | |
| Status = gBS->LocateHandle ( | |
| ByRegisterNotify, | |
| NULL, | |
| mSFSRegistration, | |
| &HandleSize, | |
| &Handle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return ; | |
| } | |
| Status = CheckStore (Handle, VAR_FILE_VOLUMEID (Dev), &Device); | |
| if (!EFI_ERROR (Status)) { | |
| break; | |
| } | |
| } | |
| VAR_FILE_DEVICEPATH (Dev) = Device; | |
| Status = OpenStore ( | |
| VAR_FILE_DEVICEPATH (Dev), | |
| VAR_FILE_FILEPATH (Dev), | |
| EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ | EFI_FILE_MODE_CREATE, | |
| &File | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| NumBytes = Dev->Size; | |
| Status = File->Write (File, &NumBytes, VAR_DATA_PTR (Dev)); | |
| ASSERT_EFI_ERROR (Status); | |
| FileClose (File); | |
| DEBUG ((EFI_D_ERROR, "FileStorage: Mapped to file!\n")); | |
| } | |
| EFI_STATUS | |
| FileStorageConstructor ( | |
| OUT VARIABLE_STORAGE **VarStore, | |
| OUT EFI_EVENT_NOTIFY *GoVirtualEvent, | |
| IN EFI_PHYSICAL_ADDRESS NvStorageBase, | |
| IN UINTN Size, | |
| IN UINT32 VolumeId, | |
| IN CHAR16 *FilePath | |
| ) | |
| { | |
| VS_DEV *Dev; | |
| EFI_STATUS Status; | |
| EFI_EVENT Event; | |
| Status = gBS->AllocatePool (EfiRuntimeServicesData, sizeof(VS_DEV), (VOID **) &Dev); | |
| ASSERT_EFI_ERROR (Status); | |
| ZeroMem (Dev, sizeof(VS_DEV)); | |
| Dev->Signature = VS_DEV_SIGNATURE; | |
| Dev->Size = Size; | |
| VAR_DATA_PTR (Dev) = (UINT8 *) (UINTN) NvStorageBase; | |
| VAR_FILE_VOLUMEID (Dev) = VolumeId; | |
| StrCpy (VAR_FILE_FILEPATH (Dev), FilePath); | |
| Dev->VarStore.Erase = FileEraseStore; | |
| Dev->VarStore.Write = FileWriteStore; | |
| DEBUG ((EFI_D_ERROR, "FileStorageConstructor(0x%0x:0x%0x): added!\n", NvStorageBase, Size)); | |
| // add notify on SFS's installation. | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| OnSimpleFileSystemInstall, | |
| Dev, | |
| &Event | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = gBS->RegisterProtocolNotify ( | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| Event, | |
| &mSFSRegistration | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| *VarStore = &Dev->VarStore; | |
| *GoVirtualEvent = OnVirtualAddressChangeFs; | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| FileEraseStore( | |
| IN VARIABLE_STORAGE *This | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VS_DEV *Dev; | |
| EFI_FILE_PROTOCOL *File; | |
| UINTN NumBytes; | |
| Status = EFI_SUCCESS; | |
| Dev = DEV_FROM_THIS(This); | |
| SetMem (VAR_DATA_PTR (Dev), Dev->Size, VAR_DEFAULT_VALUE); | |
| if (!EfiAtRuntime () && VAR_FILE_DEVICEPATH (Dev) != NULL) { | |
| Status = OpenStore ( | |
| VAR_FILE_DEVICEPATH (Dev), | |
| VAR_FILE_FILEPATH (Dev), | |
| EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, | |
| &File | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| NumBytes = Dev->Size; | |
| Status = File->Write (File, &NumBytes, VAR_DATA_PTR (Dev)); | |
| ASSERT_EFI_ERROR (Status); | |
| FileClose (File); | |
| } | |
| return Status; | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| FileWriteStore ( | |
| IN VARIABLE_STORAGE *This, | |
| IN UINTN Offset, | |
| IN UINTN BufferSize, | |
| IN VOID *Buffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VS_DEV *Dev; | |
| EFI_FILE_PROTOCOL *File; | |
| Status = EFI_SUCCESS; | |
| Dev = DEV_FROM_THIS(This); | |
| ASSERT (Buffer != NULL); | |
| ASSERT (Offset + BufferSize <= Dev->Size); | |
| CopyMem (VAR_DATA_PTR (Dev) + Offset, Buffer, BufferSize); | |
| if (!EfiAtRuntime () && VAR_FILE_DEVICEPATH (Dev) != NULL) { | |
| Status = OpenStore ( | |
| VAR_FILE_DEVICEPATH (Dev), | |
| VAR_FILE_FILEPATH (Dev), | |
| EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, | |
| &File | |
| ); | |
| Status = File->SetPosition (File, Offset); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = File->Write (File, &BufferSize, Buffer); | |
| ASSERT_EFI_ERROR (Status); | |
| FileClose (File); | |
| } | |
| return Status; | |
| } | |
| VOID | |
| EFIAPI | |
| OnVirtualAddressChangeFs ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| VS_DEV *Dev; | |
| Dev = DEV_FROM_THIS (Context); | |
| EfiConvertPointer (0, (VOID **) &VAR_DATA_PTR (Dev)); | |
| EfiConvertPointer (0, (VOID **) &Dev->VarStore.Erase); | |
| EfiConvertPointer (0, (VOID **) &Dev->VarStore.Write); | |
| } | |
| EFI_STATUS | |
| OpenStore ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *Device, | |
| IN CHAR16 *FilePathName, | |
| IN UINT64 OpenMode, | |
| OUT EFI_FILE_PROTOCOL **File | |
| ) | |
| { | |
| EFI_HANDLE Handle; | |
| EFI_FILE_HANDLE Root; | |
| EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; | |
| EFI_STATUS Status; | |
| *File = NULL; | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| &Device, | |
| &Handle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gBS->HandleProtocol ( | |
| Handle, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| (VOID **) &Volume | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Open the root directory of the volume | |
| // | |
| Root = NULL; | |
| Status = Volume->OpenVolume ( | |
| Volume, | |
| &Root | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| ASSERT (Root != NULL); | |
| // | |
| // Open file | |
| // | |
| Status = Root->Open ( | |
| Root, | |
| File, | |
| FilePathName, | |
| OpenMode, | |
| 0 | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| *File = NULL; | |
| } | |
| // | |
| // Close the Root directory | |
| // | |
| Root->Close (Root); | |
| return Status; | |
| } |