/*++

Copyright (c) 1998  Intel Corporation

Module Name:

    hand.c

Abstract:




Revision History

--*/

#include "lib.h"
#include "efistdarg.h"                        /*  !!! */


EFI_STATUS
LibLocateProtocol (
    IN  EFI_GUID    *ProtocolGuid,
    OUT VOID        **Interface
    )
/* 
 *  Find the first instance of this Protocol in the system and return it's interface
 */
{
    EFI_STATUS      Status;
    UINTN           NumberHandles, Index;
    EFI_HANDLE      *Handles;

    
    *Interface = NULL;
    Status = LibLocateHandle (ByProtocol, ProtocolGuid, NULL, &NumberHandles, &Handles);
    if (EFI_ERROR(Status)) {
        DEBUG((D_INFO, "LibLocateProtocol: Handle not found\n"));
        return Status;
    }

    for (Index=0; Index < NumberHandles; Index++) {
        Status = BS->HandleProtocol (Handles[Index], ProtocolGuid, Interface);
        if (!EFI_ERROR(Status)) {
            break;
        }
    }

    if (Handles) {
        FreePool (Handles);
    }

    return Status;
}

EFI_STATUS
LibLocateHandle (
    IN EFI_LOCATE_SEARCH_TYPE       SearchType,
    IN EFI_GUID                     *Protocol OPTIONAL,
    IN VOID                         *SearchKey OPTIONAL,
    IN OUT UINTN                    *NoHandles,
    OUT EFI_HANDLE                  **Buffer
    )

{
    EFI_STATUS          Status;
    UINTN               BufferSize;

    /* 
     *  Initialize for GrowBuffer loop
     */

    *Buffer = NULL;
    BufferSize = 50 * sizeof(EFI_HANDLE);

    /* 
     *  Call the real function
     */

    while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {

        Status = BS->LocateHandle (
                        SearchType,
                        Protocol,
                        SearchKey,
                        &BufferSize,
                        *Buffer
                        );

    }

    *NoHandles = BufferSize / sizeof (EFI_HANDLE);
    if (EFI_ERROR(Status)) {
        *NoHandles = 0;
    }

    return Status;
}

EFI_STATUS
LibLocateHandleByDiskSignature (
    IN UINT8                        MBRType,
    IN UINT8                        SignatureType,
    IN VOID                         *Signature,
    IN OUT UINTN                    *NoHandles,
    OUT EFI_HANDLE                  **Buffer
    )

{
    EFI_STATUS            Status;
    UINTN                 BufferSize;
    UINTN                 NoBlockIoHandles;
    EFI_HANDLE            *BlockIoBuffer;
    EFI_DEVICE_PATH       *DevicePath;
    UINTN                 Index;
    EFI_DEVICE_PATH       *Start, *Next, *DevPath;
    HARDDRIVE_DEVICE_PATH *HardDriveDevicePath;
    BOOLEAN               Match;
    BOOLEAN               PreviousNodeIsHardDriveDevicePath;

    /* 
     *  Initialize for GrowBuffer loop
     */

    BlockIoBuffer = NULL;
    BufferSize = 50 * sizeof(EFI_HANDLE);

    /* 
     *  Call the real function
     */

    while (GrowBuffer (&Status, (VOID **)&BlockIoBuffer, BufferSize)) {

        /* 
         *  Get list of device handles that support the BLOCK_IO Protocol.
         */

        Status = BS->LocateHandle (
                        ByProtocol,
                        &BlockIoProtocol,
                        NULL,
                        &BufferSize,
                        BlockIoBuffer
                        );

    }

    NoBlockIoHandles = BufferSize / sizeof (EFI_HANDLE);
    if (EFI_ERROR(Status)) {
        NoBlockIoHandles = 0;
    }

    /* 
     *  If there was an error or there are no device handles that support 
     *  the BLOCK_IO Protocol, then return.
     */

    if (NoBlockIoHandles == 0) {
        FreePool(BlockIoBuffer);
        *NoHandles = 0;
        *Buffer = NULL;
        return Status;
    }

    /* 
     *  Loop through all the device handles that support the BLOCK_IO Protocol
     */

    *NoHandles = 0;

    for(Index=0;Index<NoBlockIoHandles;Index++) {

        Status = BS->HandleProtocol (BlockIoBuffer[Index], 
                                     &DevicePathProtocol, 
                                     (VOID*)&DevicePath
                                     );

        /* 
         *  Search DevicePath for a Hard Drive Media Device Path node.
         *  If one is found, then see if it matches the signature that was
         *  passed in.  If it does match, and the next node is the End of the
         *  device path, and the previous node is not a Hard Drive Media Device
         *  Path, then we have found a match.
         */

        Match = FALSE;

        if (DevicePath != NULL) {

            PreviousNodeIsHardDriveDevicePath = FALSE;

            DevPath = DevicePath;
            Start = DevPath;

            /* 
             *  Check for end of device path type
             *      */

            for (; ;) {

                if ((DevicePathType(DevPath) == MEDIA_DEVICE_PATH) &&
                    (DevicePathSubType(DevPath) == MEDIA_HARDDRIVE_DP)) {

                    HardDriveDevicePath = (HARDDRIVE_DEVICE_PATH *)(DevPath);

                    if (PreviousNodeIsHardDriveDevicePath == FALSE) {

                        Next = NextDevicePathNode(DevPath);
                        if (IsDevicePathEndType(Next)) {
                            if ((HardDriveDevicePath->MBRType == MBRType) &&
                                (HardDriveDevicePath->SignatureType == SignatureType)) {
                                    switch(SignatureType) {
                                        case SIGNATURE_TYPE_MBR:
                                            if (*((UINT32 *)(Signature)) == *(UINT32 *)(&(HardDriveDevicePath->Signature[0]))) {
                                                Match = TRUE;
                                            }
                                            break;
                                        case SIGNATURE_TYPE_GUID:
                                            if (CompareGuid((EFI_GUID *)Signature,(EFI_GUID *)(&(HardDriveDevicePath->Signature[0]))) == 0) {
                                                Match = TRUE;
                                            }
                                            break;
                                    }
                            }
                        }
                    }
                    PreviousNodeIsHardDriveDevicePath = TRUE;
                } else {
                    PreviousNodeIsHardDriveDevicePath = FALSE;
                }

                if (IsDevicePathEnd(DevPath)) {
                    break;
                }

                DevPath = NextDevicePathNode(DevPath);
            }

        }

        if (Match == FALSE) {
            BlockIoBuffer[Index] = NULL;
        } else {
            *NoHandles = *NoHandles + 1;
        }
    }

    /* 
     *  If there are no matches, then return
     */

    if (*NoHandles == 0) {
        FreePool(BlockIoBuffer);
        *NoHandles = 0;
        *Buffer = NULL;
        return EFI_SUCCESS;
    }

    /* 
     *  Allocate space for the return buffer of device handles.
     */

    *Buffer = AllocatePool(*NoHandles * sizeof(EFI_HANDLE));

    if (*Buffer == NULL) {
        FreePool(BlockIoBuffer);
        *NoHandles = 0;
        *Buffer = NULL;
        return EFI_OUT_OF_RESOURCES;
    }

    /* 
     *  Build list of matching device handles.
     */

    *NoHandles = 0;
    for(Index=0;Index<NoBlockIoHandles;Index++) {
        if (BlockIoBuffer[Index] != NULL) {
            (*Buffer)[*NoHandles] = BlockIoBuffer[Index];
            *NoHandles = *NoHandles + 1;
        }
    }

    FreePool(BlockIoBuffer);

    return EFI_SUCCESS;
}

EFI_FILE_HANDLE
LibOpenRoot (
    IN EFI_HANDLE               DeviceHandle
    )
{
    EFI_STATUS                  Status;
    EFI_FILE_IO_INTERFACE       *Volume;
    EFI_FILE_HANDLE             File;


    /* 
     *  File the file system interface to the device
     */

    Status = BS->HandleProtocol (DeviceHandle, &FileSystemProtocol, (VOID*)&Volume);

    /* 
     *  Open the root directory of the volume 
     */

    if (!EFI_ERROR(Status)) {
        Status = Volume->OpenVolume(Volume, &File);
    }

    /* 
     *  Done
     */

    return EFI_ERROR(Status) ? NULL : File;
}

EFI_FILE_INFO *
LibFileInfo (
    IN EFI_FILE_HANDLE      FHand
    )
{
    EFI_STATUS              Status;
    EFI_FILE_INFO           *Buffer;
    UINTN                   BufferSize;

    /* 
     *  Initialize for GrowBuffer loop
     */

    Buffer = NULL;
    BufferSize = SIZE_OF_EFI_FILE_INFO + 200;

    /* 
     *  Call the real function
     */

    while (GrowBuffer (&Status, (VOID **) &Buffer, BufferSize)) {
        Status = FHand->GetInfo (
                    FHand,
                    &GenericFileInfo,
                    &BufferSize,
                    Buffer
                    );
    }

    return Buffer;
}

    
EFI_FILE_SYSTEM_INFO *
LibFileSystemInfo (
    IN EFI_FILE_HANDLE      FHand
    )
{
    EFI_STATUS              Status;
    EFI_FILE_SYSTEM_INFO    *Buffer;
    UINTN                   BufferSize;

    /* 
     *  Initialize for GrowBuffer loop
     */

    Buffer = NULL;
    BufferSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + 200;

    /* 
     *  Call the real function
     */

    while (GrowBuffer (&Status, (VOID **) &Buffer, BufferSize)) {
        Status = FHand->GetInfo (
                    FHand,
                    &FileSystemInfo,
                    &BufferSize,
                    Buffer
                    );
    }

    return Buffer;
}

EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *
LibFileSystemVolumeLabelInfo (
    IN EFI_FILE_HANDLE      FHand
    )
{
    EFI_STATUS                        Status;
    EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *Buffer;
    UINTN                             BufferSize;

    /* 
     *  Initialize for GrowBuffer loop
     */

    Buffer = NULL;
    BufferSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + 200;

    /* 
     *  Call the real function
     */

    while (GrowBuffer (&Status, (VOID **) &Buffer, BufferSize)) {
        Status = FHand->GetInfo (
                    FHand,
                    &FileSystemVolumeLabelInfo,
                    &BufferSize,
                    Buffer
                    );
    }

    return Buffer;
}

    

EFI_STATUS
LibInstallProtocolInterfaces (
    IN OUT EFI_HANDLE           *Handle,
    ...
    )
{
    va_list         args;
    EFI_STATUS      Status;
    EFI_GUID        *Protocol;
    VOID            *Interface;
    EFI_TPL         OldTpl;
    UINTN           Index;
    EFI_HANDLE      OldHandle;

    /* 
     *  Syncronize with notifcations
     *   */

    OldTpl = BS->RaiseTPL(TPL_NOTIFY);
    OldHandle = *Handle;

    /* 
     *  Install the protocol interfaces
     */

    Index = 0;
    Status = EFI_SUCCESS;
    va_start (args, Handle);

    while (!EFI_ERROR(Status)) {

        /* 
         *  If protocol is NULL, then it's the end of the list
         */

        Protocol = va_arg(args, EFI_GUID *);
        if (!Protocol) {
            break;
        }

        Interface = va_arg(args, VOID *);

        /* 
         *  Install it
         */

        DEBUG((D_INFO, "LibInstallProtocolInterface: %d %x\n", Protocol, Interface));
        Status = BS->InstallProtocolInterface (Handle, Protocol, EFI_NATIVE_INTERFACE, Interface);
        if (EFI_ERROR(Status)) {
            break;
        }

        Index += 1;
    }

    /* 
     *  If there was an error, remove all the interfaces that were
     *  installed without any errors
     */

    if (EFI_ERROR(Status)) {
        va_start (args, Handle);
        while (Index) {

            Protocol = va_arg(args, EFI_GUID *);
            Interface = va_arg(args, VOID *);
            BS->UninstallProtocolInterface (*Handle, Protocol, Interface);

            Index -= 1;
        }        

        *Handle = OldHandle;
    }

    /* 
     *  Done
     */

    BS->RestoreTPL(OldTpl);
    return Status;
}


VOID
LibUninstallProtocolInterfaces (
    IN EFI_HANDLE           Handle,
    ...
    )
{
    va_list         args;
    EFI_STATUS      Status;
    EFI_GUID        *Protocol;
    VOID            *Interface;

    
    va_start (args, Handle);
    for (; ;) {

        /* 
         *  If protocol is NULL, then it's the end of the list
         */

        Protocol = va_arg(args, EFI_GUID *);
        if (!Protocol) {
            break;
        }

        Interface = va_arg(args, VOID *);

        /* 
         *  Uninstall it
         */

        Status = BS->UninstallProtocolInterface (Handle, Protocol, Interface);
        if (EFI_ERROR(Status)) {
            DEBUG((D_ERROR, "LibUninstallProtocolInterfaces: failed %g, %r\n", Protocol, Handle));
        }
    }
}    


EFI_STATUS
LibReinstallProtocolInterfaces (
    IN OUT EFI_HANDLE           *Handle,
    ...
    )
{
    va_list         args;
    EFI_STATUS      Status;
    EFI_GUID        *Protocol;
    VOID            *OldInterface, *NewInterface;
    EFI_TPL         OldTpl;
    UINTN           Index;

    /* 
     *  Syncronize with notifcations
     *   */

    OldTpl = BS->RaiseTPL(TPL_NOTIFY);

    /* 
     *  Install the protocol interfaces
     */

    Index = 0;
    Status = EFI_SUCCESS;
    va_start (args, Handle);

    while (!EFI_ERROR(Status)) {

        /* 
         *  If protocol is NULL, then it's the end of the list
         */

        Protocol = va_arg(args, EFI_GUID *);
        if (!Protocol) {
            break;
        }

        OldInterface = va_arg(args, VOID *);
        NewInterface = va_arg(args, VOID *);

        /* 
         *  Reinstall it
         */

        Status = BS->ReinstallProtocolInterface (Handle, Protocol, OldInterface, NewInterface);
        if (EFI_ERROR(Status)) {
            break;
        }

        Index += 1;
    }

    /* 
     *  If there was an error, undo all the interfaces that were
     *  reinstalled without any errors
     */

    if (EFI_ERROR(Status)) {
        va_start (args, Handle);
        while (Index) {

            Protocol = va_arg(args, EFI_GUID *);
            OldInterface = va_arg(args, VOID *);
            NewInterface = va_arg(args, VOID *);

            BS->ReinstallProtocolInterface (Handle, Protocol, NewInterface, OldInterface);

            Index -= 1;
        }        
    }

    /* 
     *  Done
     */

    BS->RestoreTPL(OldTpl);
    return Status;
}