/** @file
*
*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
*
*  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.
*
**/

#ifndef _BDSINTERNAL_H_
#define _BDSINTERNAL_H_

#include <PiDxe.h>
#include <Library/BaseMemoryLib.h>
#include <Library/BdsLib.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/PcdLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>

#include <Protocol/DevicePathFromText.h>
#include <Protocol/DevicePathToText.h>

#include <Guid/GlobalVariable.h>

#define BOOT_DEVICE_DESCRIPTION_MAX   100
#define BOOT_DEVICE_FILEPATH_MAX      100
#define BOOT_DEVICE_OPTION_MAX        300
#define BOOT_DEVICE_ADDRESS_MAX       (sizeof(L"0x0000000000000000"))

#define ARM_BDS_OPTIONAL_DATA_SIGNATURE   SIGNATURE_32('a', 'b', 'o', 'd')

#define IS_ARM_BDS_BOOTENTRY(ptr)  \
  (((ptr)->OptionalData != NULL) && \
   (ReadUnaligned32 ((CONST UINT32*)&((ARM_BDS_LOADER_OPTIONAL_DATA*)((ptr)->OptionalData))->Header.Signature) \
      == ARM_BDS_OPTIONAL_DATA_SIGNATURE))

#define UPDATE_BOOT_ENTRY L"Update entry: "
#define DELETE_BOOT_ENTRY L"Delete entry: "

typedef enum {
    BDS_LOADER_EFI_APPLICATION = 0,
    BDS_LOADER_KERNEL_LINUX_ATAG,
    BDS_LOADER_KERNEL_LINUX_FDT,
} ARM_BDS_LOADER_TYPE;

typedef struct {
  UINT16                     CmdLineSize;
  UINT16                     InitrdSize;

  // These following fields have variable length and are packed:
  //CHAR8                      *CmdLine;
  //EFI_DEVICE_PATH_PROTOCOL   *InitrdPathList;
} ARM_BDS_LINUX_ARGUMENTS;

typedef union {
  ARM_BDS_LINUX_ARGUMENTS    LinuxArguments;
} ARM_BDS_LOADER_ARGUMENTS;

typedef struct {
  UINT32                     Signature;
  ARM_BDS_LOADER_TYPE        LoaderType;
} ARM_BDS_LOADER_OPTIONAL_DATA_HEADER;

typedef struct {
  ARM_BDS_LOADER_OPTIONAL_DATA_HEADER Header;
  ARM_BDS_LOADER_ARGUMENTS            Arguments;
} ARM_BDS_LOADER_OPTIONAL_DATA;

typedef struct {
  LIST_ENTRY                  Link;
  BDS_LOAD_OPTION*            BdsLoadOption;
} BDS_LOAD_OPTION_ENTRY;

typedef enum {
  BDS_DEVICE_FILESYSTEM = 0,
  BDS_DEVICE_MEMMAP,
  BDS_DEVICE_PXE,
  BDS_DEVICE_TFTP,
  BDS_DEVICE_MAX
} BDS_SUPPORTED_DEVICE_TYPE;

typedef struct {
  LIST_ENTRY                          Link;
  CHAR16                              Description[BOOT_DEVICE_DESCRIPTION_MAX];
  EFI_DEVICE_PATH_PROTOCOL*           DevicePathProtocol;
  struct _BDS_LOAD_OPTION_SUPPORT*    Support;
} BDS_SUPPORTED_DEVICE;

#define SUPPORTED_BOOT_DEVICE_FROM_LINK(a)   BASE_CR(a, BDS_SUPPORTED_DEVICE, Link)

typedef struct _BDS_LOAD_OPTION_SUPPORT {
  BDS_SUPPORTED_DEVICE_TYPE   Type;
  EFI_STATUS    (*ListDevices)(IN OUT LIST_ENTRY* BdsLoadOptionList);
  BOOLEAN       (*IsSupported)(IN  EFI_DEVICE_PATH *DevicePath);
  EFI_STATUS    (*CreateDevicePathNode)(IN CHAR16* FileName, OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNodes, OUT BOOLEAN *RequestBootType);
  EFI_STATUS    (*UpdateDevicePathNode)(IN EFI_DEVICE_PATH *OldDevicePath, IN CHAR16* FileName, OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, OUT BOOLEAN *RequestBootType);
} BDS_LOAD_OPTION_SUPPORT;

#define LOAD_OPTION_ENTRY_FROM_LINK(a)  BASE_CR(a, BDS_LOAD_OPTION_ENTRY, Link)
#define LOAD_OPTION_FROM_LINK(a)        ((BDS_LOAD_OPTION_ENTRY*)BASE_CR(a, BDS_LOAD_OPTION_ENTRY, Link))->BdsLoadOption

EFI_STATUS
BootDeviceListSupportedInit (
  IN OUT LIST_ENTRY *SupportedDeviceList
  );

EFI_STATUS
BootDeviceListSupportedFree (
  IN LIST_ENTRY *SupportedDeviceList,
  IN BDS_SUPPORTED_DEVICE *Except
  );

EFI_STATUS
BootDeviceGetDeviceSupport (
  IN  EFI_DEVICE_PATH           *DevicePath,
  OUT BDS_LOAD_OPTION_SUPPORT   **DeviceSupport
  );

EFI_STATUS
GetHIInputStr (
  IN OUT CHAR16  *CmdLine,
  IN     UINTN   MaxCmdLine
  );

EFI_STATUS
EditHIInputStr (
  IN OUT CHAR16  *CmdLine,
  IN     UINTN   MaxCmdLine
  );

EFI_STATUS
GetHIInputAscii (
  IN OUT CHAR8   *CmdLine,
  IN     UINTN   MaxCmdLine
  );

EFI_STATUS
EditHIInputAscii (
  IN OUT CHAR8   *CmdLine,
  IN     UINTN   MaxCmdLine
  );

EFI_STATUS
GetHIInputInteger (
  IN OUT UINTN   *Integer
  );

EFI_STATUS
GetHIInputIP (
  OUT EFI_IP_ADDRESS   *Ip
  );

EFI_STATUS
GetHIInputBoolean (
  OUT BOOLEAN *Value
  );

BOOLEAN
HasFilePathEfiExtension (
  IN CHAR16* FilePath
  );

EFI_DEVICE_PATH*
GetLastDevicePathNode (
  IN EFI_DEVICE_PATH*  DevicePath
  );

EFI_STATUS
BdsStartBootOption (
  IN CHAR16* BootOption
  );

UINTN
GetUnalignedDevicePathSize (
  IN EFI_DEVICE_PATH* DevicePath
  );

EFI_DEVICE_PATH*
GetAlignedDevicePath (
  IN EFI_DEVICE_PATH* DevicePath
  );

EFI_STATUS
GenerateDeviceDescriptionName (
  IN  EFI_HANDLE  Handle,
  IN OUT CHAR16*  Description
  );

EFI_STATUS
BootOptionList (
  IN OUT LIST_ENTRY *BootOptionList
  );

EFI_STATUS
BootOptionParseLoadOption (
  IN  EFI_LOAD_OPTION EfiLoadOption,
  IN  UINTN           EfiLoadOptionSize,
  OUT BDS_LOAD_OPTION **BdsLoadOption
  );

EFI_STATUS
BootOptionStart (
  IN BDS_LOAD_OPTION *BootOption
  );

EFI_STATUS
BootOptionCreate (
  IN  UINT32                    Attributes,
  IN  CHAR16*                   BootDescription,
  IN  EFI_DEVICE_PATH_PROTOCOL* DevicePath,
  IN  ARM_BDS_LOADER_TYPE       BootType,
  IN  UINT8*                    OptionalData,
  IN  UINTN                     OptionalDataSize,
  OUT BDS_LOAD_OPTION**         BdsLoadOption
  );

EFI_STATUS
BootOptionUpdate (
  IN  BDS_LOAD_OPTION*          BdsLoadOption,
  IN  UINT32                    Attributes,
  IN  CHAR16*                   BootDescription,
  IN  EFI_DEVICE_PATH_PROTOCOL* DevicePath,
  IN  ARM_BDS_LOADER_TYPE       BootType,
  IN UINT8*                     OptionalData,
  IN UINTN                      OptionalDataSize
  );

EFI_STATUS
BootOptionDelete (
  IN  BDS_LOAD_OPTION *BootOption
  );

EFI_STATUS
BootDeviceGetType (
  IN  EFI_DEVICE_PATH* DevicePath,
  OUT ARM_BDS_LOADER_TYPE *BootType,
  OUT UINT32 *Attributes
  );

EFI_STATUS
BootMenuMain (
  VOID
  );

BOOLEAN
IsUnicodeString (
  IN VOID* String
  );

/*
 * Try to detect if the given string is an ASCII or Unicode string
 *
 * There are actually few limitation to this function but it is mainly to give
 * a user friendly output.
 *
 * Some limitations:
 *   - it only supports unicode string that use ASCII character (< 0x100)
 *   - single character ASCII strings are interpreted as Unicode string
 *   - string cannot be longer than 2 x BOOT_DEVICE_OPTION_MAX (600 bytes)
 *
 * @param String    Buffer that might contain a Unicode or Ascii string
 * @param IsUnicode If not NULL this boolean value returns if the string is an
 *                  ASCII or Unicode string.
 */
BOOLEAN
IsPrintableString (
  IN  VOID*    String,
  OUT BOOLEAN *IsUnicode
  );

#endif /* _BDSINTERNAL_H_ */
