/* $Id: fsw_efi.c 29125 2010-05-06 09:43:05Z vboxsync $ */ /** @file * fsw_efi.c - EFI host environment code. */ /* * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /*- * This code is based on: * * Copyright (c) 2006 Christoph Pfisterer * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * * Neither the name of Christoph Pfisterer nor the names of the * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fsw_efi.h" #define DEBUG_LEVEL 0 #ifndef FSTYPE #ifdef VBOX #error FSTYPE must be defined! #else #define FSTYPE ext2 #endif #endif #define DEBUG_VBFS 0 CHAR8 *msgCursor; MESSAGE_LOG_PROTOCOL *Msg = NULL; #if DEBUG_VBFS==2 #define DBG(...) AsciiPrint(__VA_ARGS__) #elif DEBUG_VBFS==1 #define DBG(...) BootLog(__VA_ARGS__) #else #define DBG(...) #endif /** Helper macro for stringification. */ #define FSW_EFI_STRINGIFY(x) L ## #x /** Expands to the EFI driver name given the file system type name. */ #define FSW_EFI_DRIVER_NAME(t) L"Clover " FSW_EFI_STRINGIFY(t) L" File System Driver" // function prototypes EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath); EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath); EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer); EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, IN CHAR8 *Language, OUT CHAR16 **DriverName); EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_HANDLE ChildHandle OPTIONAL, IN CHAR8 *Language, OUT CHAR16 **ControllerName); void fsw_efi_change_blocksize(struct fsw_volume *vol, fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize); fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer); EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume); EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This, OUT EFI_FILE_PROTOCOL **Root); EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno, OUT EFI_FILE_PROTOCOL **NewFileHandle); EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File, IN OUT UINTN *BufferSize, OUT VOID *Buffer); EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File, OUT UINT64 *Position); EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File, IN UINT64 Position); EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File, OUT EFI_FILE_PROTOCOL **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes); EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File, IN OUT UINTN *BufferSize, OUT VOID *Buffer); EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File, IN UINT64 Position); EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer); EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, IN struct fsw_dnode *dno, IN OUT UINTN *BufferSize, OUT VOID *Buffer); /** * Interface structure for the EFI Driver Binding protocol. */ EFI_DRIVER_BINDING_PROTOCOL fsw_efi_DriverBinding_table = { fsw_efi_DriverBinding_Supported, fsw_efi_DriverBinding_Start, fsw_efi_DriverBinding_Stop, 0x00, NULL, NULL }; /** * Interface structure for the EFI Component Name protocol. */ EFI_COMPONENT_NAME_PROTOCOL fsw_efi_ComponentName_table = { fsw_efi_ComponentName_GetDriverName, fsw_efi_ComponentName_GetControllerName, "eng" }; GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL fsw_efi_ComponentName2_table = { (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) fsw_efi_ComponentName_GetDriverName, (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) fsw_efi_ComponentName_GetControllerName, "en" }; EFI_LOCK fsw_efi_Lock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_CALLBACK); /** * Dispatch table for our FSW host driver. */ struct fsw_host_table fsw_efi_host_table = { FSW_STRING_TYPE_UTF16, fsw_efi_change_blocksize, fsw_efi_read_block }; extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME ( FSTYPE ); EFI_STATUS fsw_efi_AcquireLockOrFail ( VOID ) { return EfiAcquireLockOrFail (&fsw_efi_Lock); } VOID fsw_efi_ReleaseLock ( VOID ) { EfiReleaseLock (&fsw_efi_Lock); } /** * Image entry point. Installs the Driver Binding and Component Name protocols * on the image's handle. Actually mounting a file system is initiated through * the Driver Binding protocol at the firmware's request. */ EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) { EFI_STATUS Status; #ifndef VBOX InitializeLib(ImageHandle, SystemTable); #endif // complete Driver Binding protocol instance fsw_efi_DriverBinding_table.ImageHandle = ImageHandle; fsw_efi_DriverBinding_table.DriverBindingHandle = ImageHandle; // install Driver Binding protocol Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, &PROTO_NAME(DriverBindingProtocol), EFI_NATIVE_INTERFACE, &fsw_efi_DriverBinding_table); if (EFI_ERROR (Status)) { return Status; } // install Component Name 2 protocol Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, &PROTO_NAME(ComponentName2Protocol), EFI_NATIVE_INTERFACE, &fsw_efi_ComponentName_table); if (EFI_ERROR(Status)) { // Print(L"Failed to install Component Name 2 Protocol\n"); } // install Component Name protocol Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, &PROTO_NAME(ComponentNameProtocol), EFI_NATIVE_INTERFACE, &fsw_efi_ComponentName_table); if (EFI_ERROR (Status)) { return Status; } // OverrideFunctions(); /* Msg = NULL; msgCursor = NULL; Status = BS->LocateProtocol(&gMsgLogProtocolGuid, NULL, (VOID **) &Msg); if (!EFI_ERROR(Status) && (Msg != NULL)) { msgCursor = Msg->Cursor; BootLog("MsgLog installed into VBoxFs\n"); } */ return EFI_SUCCESS; } /** * Driver Binding EFI protocol, Supported function. This function is called by EFI * to test if this driver can handle a certain device. Our implementation only checks * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols) * and implicitly checks if the disk is already in use by another driver. */ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath) { EFI_STATUS Status; EFI_DISK_IO_PROTOCOL *DiskIo; EFI_DISK_IO2_PROTOCOL *DiskIo2; // we check for both DiskIO and BlockIO protocols // both V1 and V2 // first, open DiskIO2 Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(DiskIo2Protocol), (VOID **) &DiskIo2, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); if (EFI_ERROR(Status)) { DiskIo2 = NULL; } BS->CloseProtocol(ControllerHandle, &PROTO_NAME(DiskIo2Protocol), This->DriverBindingHandle, ControllerHandle); // next, open DiskIO Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(DiskIoProtocol), (VOID **) &DiskIo, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); if (EFI_ERROR(Status)) return Status; // we were just checking, close it again BS->CloseProtocol(ControllerHandle, &PROTO_NAME(DiskIoProtocol), This->DriverBindingHandle, ControllerHandle); // next, check BlockIO2 without actually opening it Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(BlockIo2Protocol), NULL, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); // next, check BlockIO without actually opening it Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(BlockIoProtocol), NULL, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); return Status; } /** * Driver Binding EFI protocol, Start function. This function is called by EFI * to start driving the given device. It is still possible at this point to * return EFI_UNSUPPORTED, and in fact we will do so if the file system driver * cannot find the superblock signature (or equivalent) that it expects. * * This function allocates memory for a per-volume structure, opens the * required protocols (just Disk I/O in our case, Block I/O is only looked * at to get the MediaId field), and lets the FSW core mount the file system. * If successful, an EFI Simple File System protocol is exported on the * device handle. */ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath) { EFI_STATUS Status; EFI_BLOCK_IO_PROTOCOL *BlockIo; EFI_BLOCK_IO2_PROTOCOL *BlockIo2; EFI_DISK_IO_PROTOCOL *DiskIo; EFI_DISK_IO2_PROTOCOL *DiskIo2; FSW_VOLUME_DATA *Volume; #if DEBUG_LEVEL // Print(L"fsw_efi_DriverBinding_Start\n"); #endif // open consumed protocols Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(BlockIo2Protocol), (VOID **) &BlockIo2, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(Status)) { BlockIo2 = NULL; } Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(BlockIoProtocol), (VOID **) &BlockIo, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); // NOTE: we only want to look at the MediaId if ((BlockIo2 == NULL) && EFI_ERROR(Status)) { // Print(L"Fsw ERROR: OpenProtocol(BlockIo) returned %x\n", Status); return Status; } Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(DiskIo2Protocol), (VOID **) &DiskIo2, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); if (EFI_ERROR(Status)) { DiskIo2 = NULL; } Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(DiskIoProtocol), (VOID **) &DiskIo, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); if ((DiskIo2 == NULL) && EFI_ERROR(Status)) { // DBG("Fsw ERROR: OpenProtocol(DiskIo) returned %r\n", Status); return Status; } // allocate volume structure Volume = AllocateZeroPool(sizeof(FSW_VOLUME_DATA)); Volume->Signature = FSW_VOLUME_DATA_SIGNATURE; Volume->Handle = ControllerHandle; Volume->DiskIo = DiskIo; Volume->DiskIo2 = DiskIo2; Volume->LastIOStatus = EFI_SUCCESS; if (BlockIo2 != NULL) { Volume->MediaId = BlockIo2->Media->MediaId; } else { Volume->MediaId = BlockIo->Media->MediaId; } // mount the filesystem Status = fsw_efi_map_status(fsw_mount(Volume, &fsw_efi_host_table, &FSW_FSTYPE_TABLE_NAME(FSTYPE), &Volume->vol), Volume); if (!EFI_ERROR(Status)) { // register the SimpleFileSystem protocol Volume->FileSystem.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION; Volume->FileSystem.OpenVolume = fsw_efi_FileSystem_OpenVolume; Status = BS->InstallMultipleProtocolInterfaces( &ControllerHandle, &PROTO_NAME(SimpleFileSystemProtocol), &Volume->FileSystem, NULL); if (EFI_ERROR(Status)) { // Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status); } } // on errors, close the opened protocols if (EFI_ERROR(Status)) { if (Volume->vol != NULL) fsw_unmount(Volume->vol); FreePool(Volume); BS->CloseProtocol(ControllerHandle, &PROTO_NAME(DiskIo2Protocol), This->DriverBindingHandle, ControllerHandle); BS->CloseProtocol(ControllerHandle, &PROTO_NAME(DiskIoProtocol), This->DriverBindingHandle, ControllerHandle); } return Status; } /** * Driver Binding EFI protocol, Stop function. This function is called by EFI * to stop the driver on the given device. This translates to an unmount * call for the FSW core. * * We assume that all file handles on the volume have been closed before * the driver is stopped. At least with the EFI shell, that is actually the * case; it closes all file handles between commands. */ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer) { EFI_STATUS Status; EFI_FILE_IO_INTERFACE *FileSystem; FSW_VOLUME_DATA *Volume; #if DEBUG_LEVEL // Print(L"fsw_efi_DriverBinding_Stop\n"); #endif // get the installed SimpleFileSystem interface Status = BS->OpenProtocol(ControllerHandle, &PROTO_NAME(SimpleFileSystemProtocol), (VOID **) &FileSystem, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(Status)) return EFI_UNSUPPORTED; // get private data structure Volume = FSW_VOLUME_FROM_FILE_SYSTEM(FileSystem); // uninstall Simple File System protocol Status = BS->UninstallMultipleProtocolInterfaces(ControllerHandle, &PROTO_NAME(SimpleFileSystemProtocol), &Volume->FileSystem, NULL); if (EFI_ERROR(Status)) { // Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status); return Status; } #if DEBUG_LEVEL // Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n"); #endif // release private data structure if (Volume->vol != NULL) fsw_unmount(Volume->vol); FreePool(Volume); Status = BS->CloseProtocol(ControllerHandle, &PROTO_NAME(DiskIo2Protocol), This->DriverBindingHandle, ControllerHandle); // close the consumed protocols Status = BS->CloseProtocol(ControllerHandle, &PROTO_NAME(DiskIoProtocol), This->DriverBindingHandle, ControllerHandle); return Status; } /** * Component Name EFI protocol, GetDriverName function. Used by the EFI * environment to inquire the name of this driver. The name returned is * based on the file system type actually used in compilation. */ EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, IN CHAR8 *Language, OUT CHAR16 **DriverName) { if (Language == NULL || DriverName == NULL) return EFI_INVALID_PARAMETER; //#if 0 *DriverName = FSW_EFI_DRIVER_NAME(FSTYPE); return EFI_SUCCESS; } /** * Component Name EFI protocol, GetControllerName function. Not implemented * because this is not a "bus" driver in the sense of the EFI Driver Model. */ EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_HANDLE ChildHandle OPTIONAL, IN CHAR8 *Language, OUT CHAR16 **ControllerName) { return EFI_UNSUPPORTED; } /** * FSW interface function for block size changes. This function is called by the FSW core * when the file system driver changes the block sizes for the volume. */ void fsw_efi_change_blocksize(struct fsw_volume *vol, fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize) { // nothing to do } /** * FSW interface function to read data blocks. This function is called by the FSW core * to read a block of data from the device. The buffer is allocated by the core code. */ fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer) { EFI_STATUS Status; FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)vol->host_data; // FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_efi_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize)); // read from disk if (Volume->DiskIo2 != NULL) { Status = Volume->DiskIo2->ReadDiskEx(Volume->DiskIo2, Volume->MediaId, (UINT64)phys_bno * vol->phys_blocksize, &(Volume->DiskIo2Token), vol->phys_blocksize, buffer); } else { Status = Volume->DiskIo->ReadDisk(Volume->DiskIo, Volume->MediaId, (UINT64)phys_bno * vol->phys_blocksize, vol->phys_blocksize, buffer); } Volume->LastIOStatus = Status; if (EFI_ERROR(Status)) return FSW_IO_ERROR; return FSW_SUCCESS; } /** * Map FSW status codes to EFI status codes. The FSW_IO_ERROR code is only produced * by fsw_efi_read_block, so we map it back to the EFI status code remembered from * the last I/O operation. */ EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume) { switch (fsw_status) { case FSW_SUCCESS: return EFI_SUCCESS; case FSW_OUT_OF_MEMORY: return EFI_VOLUME_CORRUPTED; case FSW_IO_ERROR: return Volume->LastIOStatus; case FSW_UNSUPPORTED: return EFI_UNSUPPORTED; case FSW_NOT_FOUND: return EFI_NOT_FOUND; case FSW_VOLUME_CORRUPTED: return EFI_VOLUME_CORRUPTED; default: return EFI_DEVICE_ERROR; } } /** * File System EFI protocol, OpenVolume function. Creates a file handle for * the root directory and returns it. Note that this function may be called * multiple times and returns a new file handle each time. Each returned * handle is closed by the client using it. */ EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This, OUT EFI_FILE_PROTOCOL **Root) { EFI_STATUS Status; FSW_VOLUME_DATA *Volume = FSW_VOLUME_FROM_FILE_SYSTEM(This); #if DEBUG_LEVEL // Print(L"fsw_efi_FileSystem_OpenVolume\n"); #endif Status = fsw_efi_dnode_to_FileHandle(Volume->vol->root, Root); return Status; } /** * File Handle EFI protocol, Open function. Dispatches the call * based on the kind of file handle. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_Open(IN EFI_FILE_PROTOCOL *This, OUT EFI_FILE_PROTOCOL **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes) { FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); if (File->Type == FSW_EFI_FILE_TYPE_DIR) return fsw_efi_dir_open(File, NewHandle, FileName, OpenMode, Attributes); // not supported for regular files return EFI_UNSUPPORTED; } EFI_STATUS EFIAPI fsw_efi_FileHandle_OpenEx(IN EFI_FILE_PROTOCOL *This, OUT EFI_FILE_PROTOCOL **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes, IN OUT EFI_FILE_IO_TOKEN *Token) { return fsw_efi_FileHandle_Open(This, NewHandle, FileName, OpenMode, Attributes); } /** * File Handle EFI protocol, Close function. Closes the FSW shandle * and frees the memory used for the structure. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_Close(IN EFI_FILE_PROTOCOL *This) { FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); #if DEBUG_LEVEL // Print(L"fsw_efi_FileHandle_Close\n"); #endif fsw_shandle_close(&File->shand); FreePool(File); return EFI_SUCCESS; } /** * File Handle EFI protocol, Delete function. Calls through to Close * and returns a warning because this driver is read-only. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_Delete(IN EFI_FILE_PROTOCOL *This) { EFI_STATUS Status; Status = This->Close(This); if (Status == EFI_SUCCESS) { // this driver is read-only Status = EFI_WARN_DELETE_FAILURE; } return Status; } /** * File Handle EFI protocol, Read function. Dispatches the call * based on the kind of file handle. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_Read(IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer) { FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); if (File->Type == FSW_EFI_FILE_TYPE_FILE) return fsw_efi_file_read(File, BufferSize, Buffer); else if (File->Type == FSW_EFI_FILE_TYPE_DIR) return fsw_efi_dir_read(File, BufferSize, Buffer); return EFI_UNSUPPORTED; } EFI_STATUS EFIAPI fsw_efi_FileHandle_ReadEx(IN EFI_FILE_PROTOCOL *This, IN OUT EFI_FILE_IO_TOKEN *Token) { return fsw_efi_FileHandle_Read(This, &Token->BufferSize, Token->Buffer); }; /** * File Handle EFI protocol, Write function. Returns unsupported status * because this driver is read-only. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_Write(IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer) { // this driver is read-only return EFI_WRITE_PROTECTED; } EFI_STATUS EFIAPI fsw_efi_FileHandle_WriteEx(IN EFI_FILE_PROTOCOL *This, IN OUT EFI_FILE_IO_TOKEN *Token) { return fsw_efi_FileHandle_Write(This, &Token->BufferSize, Token->Buffer); } /** * File Handle EFI protocol, GetPosition function. Dispatches the call * based on the kind of file handle. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_GetPosition(IN EFI_FILE_PROTOCOL *This, OUT UINT64 *Position) { FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); if (File->Type == FSW_EFI_FILE_TYPE_FILE) return fsw_efi_file_getpos(File, Position); // not defined for directories return EFI_UNSUPPORTED; } /** * File Handle EFI protocol, SetPosition function. Dispatches the call * based on the kind of file handle. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_SetPosition(IN EFI_FILE_PROTOCOL *This, IN UINT64 Position) { FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); if (File->Type == FSW_EFI_FILE_TYPE_FILE) return fsw_efi_file_setpos(File, Position); else if (File->Type == FSW_EFI_FILE_TYPE_DIR) return fsw_efi_dir_setpos(File, Position); return EFI_UNSUPPORTED; } /** * File Handle EFI protocol, GetInfo function. Dispatches to the common * function implementing this. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_GetInfo(IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer) { FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); return fsw_efi_dnode_getinfo(File, InformationType, BufferSize, Buffer); } /** * File Handle EFI protocol, SetInfo function. Returns unsupported status * because this driver is read-only. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_SetInfo(IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN UINTN BufferSize, IN VOID *Buffer) { // this driver is read-only return EFI_WRITE_PROTECTED; } /** * File Handle EFI protocol, Flush function. Returns unsupported status * because this driver is read-only. */ EFI_STATUS EFIAPI fsw_efi_FileHandle_Flush(IN EFI_FILE_PROTOCOL *This) { // this driver is read-only return EFI_WRITE_PROTECTED; } EFI_STATUS EFIAPI fsw_efi_FileHandle_FlushEx(IN EFI_FILE_PROTOCOL *This, IN OUT EFI_FILE_IO_TOKEN *Token) { return fsw_efi_FileHandle_Flush(This); } /** * Set up a file handle for a dnode. This function allocates a data structure * for a file handle, opens a FSW shandle and populates the EFI_FILE_PROTOCOL structure * with the interface functions. */ EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno, OUT EFI_FILE_PROTOCOL **NewFileHandle) { EFI_STATUS Status; FSW_FILE_DATA *File; // make sure the dnode has complete info Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data); if (EFI_ERROR(Status)) return Status; // check type if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR) return EFI_UNSUPPORTED; // allocate file structure File = AllocateZeroPool(sizeof(FSW_FILE_DATA)); File->Signature = FSW_FILE_DATA_SIGNATURE; if (dno->type == FSW_DNODE_TYPE_FILE) File->Type = FSW_EFI_FILE_TYPE_FILE; else if (dno->type == FSW_DNODE_TYPE_DIR) File->Type = FSW_EFI_FILE_TYPE_DIR; // open shandle Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand), (FSW_VOLUME_DATA *)dno->vol->host_data); if (EFI_ERROR(Status)) { FreePool(File); return Status; } // populate the file handle File->FileHandle.Revision = EFI_FILE_HANDLE_REVISION; File->FileHandle.Open = fsw_efi_FileHandle_Open; File->FileHandle.Close = fsw_efi_FileHandle_Close; File->FileHandle.Delete = fsw_efi_FileHandle_Delete; File->FileHandle.Read = fsw_efi_FileHandle_Read; File->FileHandle.Write = fsw_efi_FileHandle_Write; File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition; File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition; File->FileHandle.GetInfo = fsw_efi_FileHandle_GetInfo; File->FileHandle.SetInfo = fsw_efi_FileHandle_SetInfo; File->FileHandle.Flush = fsw_efi_FileHandle_Flush; File->FileHandle.OpenEx = fsw_efi_FileHandle_OpenEx; File->FileHandle.ReadEx = fsw_efi_FileHandle_ReadEx; File->FileHandle.WriteEx = fsw_efi_FileHandle_WriteEx; File->FileHandle.FlushEx = fsw_efi_FileHandle_FlushEx; *NewFileHandle = &File->FileHandle; return EFI_SUCCESS; } /** * Data read function for regular files. Calls through to fsw_shandle_read. */ EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File, IN OUT UINTN *BufferSize, OUT VOID *Buffer) { EFI_STATUS Status; fsw_u32 buffer_size; #if DEBUG_LEVEL Print(L"fsw_efi_file_read %d bytes\n", *BufferSize); #endif buffer_size = (fsw_u32)*BufferSize; Status = fsw_efi_map_status(fsw_shandle_read(&File->shand, &buffer_size, Buffer), (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data); *BufferSize = buffer_size; return Status; } /** * Get file position for regular files. */ EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File, OUT UINT64 *Position) { *Position = File->shand.pos; return EFI_SUCCESS; } /** * Set file position for regular files. EFI specifies the all-ones value * to be a special value for the end of the file. */ EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File, IN UINT64 Position) { if (Position == 0xFFFFFFFFFFFFFFFFULL) File->shand.pos = File->shand.dnode->size; else File->shand.pos = Position; return EFI_SUCCESS; } /** * Open function used to open new file handles relative to a directory. * In EFI, the "open file" function is implemented by directory file handles * and is passed a relative or volume-absolute path to the file or directory * to open. We use fsw_dnode_lookup_path to find the node plus an additional * call to fsw_dnode_resolve because EFI has no concept of symbolic links. */ EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File, OUT EFI_FILE_PROTOCOL **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes) { EFI_STATUS Status; FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data; struct fsw_dnode *dno; struct fsw_dnode *target_dno; struct fsw_string lookup_path; #if DEBUG_LEVEL Print(L"fsw_efi_dir_open: '%s'\n", FileName); #endif if (OpenMode != EFI_FILE_MODE_READ) return EFI_WRITE_PROTECTED; lookup_path.type = FSW_STRING_TYPE_UTF16; lookup_path.len = (int)StrLen(FileName); lookup_path.size = lookup_path.len * sizeof(fsw_u16); lookup_path.data = FileName; // resolve the path (symlinks along the way are automatically resolved) Status = fsw_efi_map_status(fsw_dnode_lookup_path(File->shand.dnode, &lookup_path, '\\', &dno), Volume); if (EFI_ERROR(Status)) return Status; // if the final node is a symlink, also resolve it Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno), Volume); fsw_dnode_release(dno); if (EFI_ERROR(Status)) return Status; dno = target_dno; // make a new EFI handle for the target dnode Status = fsw_efi_dnode_to_FileHandle(dno, NewHandle); fsw_dnode_release(dno); return Status; } /** * Read function for directories. A file handle read on a directory retrieves * the next directory entry. */ EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File, IN OUT UINTN *BufferSize, OUT VOID *Buffer) { EFI_STATUS Status; FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data; struct fsw_dnode *dno; #if DEBUG_LEVEL Print(L"fsw_efi_dir_read...\n"); #endif // read the next entry Status = fsw_efi_map_status(fsw_dnode_dir_read(&File->shand, &dno), Volume); if (Status == EFI_NOT_FOUND) { // end of directory *BufferSize = 0; #if DEBUG_LEVEL Print(L"...no more entries\n"); #endif return EFI_SUCCESS; } if (EFI_ERROR(Status)) return Status; // get info into buffer Status = fsw_efi_dnode_fill_FileInfo(Volume, dno, BufferSize, Buffer); fsw_dnode_release(dno); return Status; } /** * Set file position for directories. The only allowed set position operation * for directories is to rewind the directory completely by setting the * position to zero. */ EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File, IN UINT64 Position) { if (Position == 0) { File->shand.pos = 0; return EFI_SUCCESS; } else { // directories can only rewind to the start return EFI_UNSUPPORTED; } } /** * Get file or volume information. This function implements the GetInfo call * for all file handles. Control is dispatched according to the type of information * requested by the caller. */ EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer) { EFI_STATUS Status; FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data; EFI_FILE_SYSTEM_INFO *FSInfo; UINTN RequiredSize; struct fsw_volume_stat vsb; if (CompareGuid(InformationType, &GUID_NAME(FileInfo))) { #if DEBUG_LEVEL Print(L"fsw_efi_dnode_getinfo: FILE_INFO\n"); #endif Status = fsw_efi_dnode_fill_FileInfo(Volume, File->shand.dnode, BufferSize, Buffer); } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemInfo))) { #if DEBUG_LEVEL Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n"); #endif // check buffer size RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + fsw_efi_strsize(&Volume->vol->label); if (*BufferSize < RequiredSize) { *BufferSize = RequiredSize; return EFI_BUFFER_TOO_SMALL; } // fill structure FSInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; FSInfo->Size = RequiredSize; FSInfo->ReadOnly = TRUE; FSInfo->BlockSize = Volume->vol->log_blocksize; fsw_efi_strcpy(FSInfo->VolumeLabel, &Volume->vol->label); // get the missing info from the fs driver ZeroMem(&vsb, sizeof(struct fsw_volume_stat)); Status = fsw_efi_map_status(fsw_volume_stat(Volume->vol, &vsb), Volume); if (EFI_ERROR(Status)) return Status; FSInfo->VolumeSize = vsb.total_bytes; FSInfo->FreeSpace = vsb.free_bytes; // prepare for return *BufferSize = RequiredSize; Status = EFI_SUCCESS; } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemVolumeLabelInfoId))) { #if DEBUG_LEVEL Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n"); #endif // check buffer size RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + fsw_efi_strsize(&Volume->vol->label); if (*BufferSize < RequiredSize) { *BufferSize = RequiredSize; return EFI_BUFFER_TOO_SMALL; } // copy volume label fsw_efi_strcpy(((EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Buffer)->VolumeLabel, &Volume->vol->label); // prepare for return *BufferSize = RequiredSize; Status = EFI_SUCCESS; } else { Status = EFI_UNSUPPORTED; } return Status; } /** * Time mapping callback for the fsw_dnode_stat call. This function converts * a Posix style timestamp into an EFI_TIME structure and writes it to the * appropriate member of the EFI_FILE_INFO structure that we're filling. */ static void fsw_efi_store_time_posix(struct fsw_dnode_stat_str *sb, int which, fsw_u32 posix_time) { EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; if (which == FSW_DNODE_STAT_CTIME) fsw_efi_decode_time(&FileInfo->CreateTime, posix_time); else if (which == FSW_DNODE_STAT_MTIME) fsw_efi_decode_time(&FileInfo->ModificationTime, posix_time); else if (which == FSW_DNODE_STAT_ATIME) fsw_efi_decode_time(&FileInfo->LastAccessTime, posix_time); } /** * Mode mapping callback for the fsw_dnode_stat call. This function looks at * the Posix mode passed by the file system driver and makes appropriate * adjustments to the EFI_FILE_INFO structure that we're filling. */ static void fsw_efi_store_attr_posix(struct fsw_dnode_stat_str *sb, fsw_u16 posix_mode) { EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; if ((posix_mode & S_IWUSR) == 0) FileInfo->Attribute |= EFI_FILE_READ_ONLY; } // // Common function to fill an EFI_FILE_INFO with information about a dnode. // there is caller responsible to allocate buffer // EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, IN struct fsw_dnode *dno, IN OUT UINTN *BufferSize, OUT VOID *Buffer) { EFI_STATUS Status; EFI_FILE_INFO *FileInfo; UINTN RequiredSize; struct fsw_dnode_stat_str sb; struct fsw_dnode *target_dno; // make sure the dnode has complete info Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume); if (EFI_ERROR(Status)) return Status; // TODO: check/assert that the dno's name is in UTF16 // check buffer size RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name); if (*BufferSize < RequiredSize) { // TODO: wind back the directory in this case #if DEBUG_LEVEL Print(L"...BUFFER TOO SMALL\n"); #endif *BufferSize = RequiredSize; return EFI_BUFFER_TOO_SMALL; } // fill structure ZeroMem (Buffer, RequiredSize); FileInfo = (EFI_FILE_INFO *) Buffer; // Use original name (name of symlink if any) fsw_efi_strcpy (FileInfo->FileName, &dno->name); // if the node is a symlink, resolve it Status = fsw_efi_map_status (fsw_dnode_resolve (dno, &target_dno), Volume); fsw_dnode_release (dno); if (EFI_ERROR (Status)) return Status; dno = target_dno; // make sure the dnode has complete info Status = fsw_efi_map_status (fsw_dnode_fill (dno), Volume); if (EFI_ERROR (Status)) return Status; FileInfo->Size = RequiredSize; FileInfo->FileSize = dno->size; FileInfo->Attribute = EFI_FILE_READ_ONLY; if (dno->type == FSW_DNODE_TYPE_DIR) FileInfo->Attribute |= EFI_FILE_DIRECTORY; // get the missing info from the fs driver ZeroMem(&sb, sizeof(struct fsw_dnode_stat_str)); sb.store_time_posix = fsw_efi_store_time_posix; sb.store_attr_posix = fsw_efi_store_attr_posix; sb.host_data = FileInfo; Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume); if (EFI_ERROR(Status)) return Status; FileInfo->PhysicalSize = sb.used_bytes; // prepare for return *BufferSize = RequiredSize; #if DEBUG_LEVEL Print(L"...returning '%s'\n", FileInfo->FileName); #endif return EFI_SUCCESS; } // EOF