/** @file
  FIT Load Image Support
Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "FitLib.h"

PROPERTY_DATA  PropertyData32List[] = {
  { "data-offset", PAYLOAD_ENTRY_OFFSET_OFFSET  },
  { "data-size",   PAYLOAD_ENTRY_SIZE_OFFSET    },
  { "reloc-start", RELOCATE_TABLE_OFFSET_OFFSET }
};

PROPERTY_DATA  PropertyData64List[] = {
  { "entry-start", PAYLOAD_ENTRY_POINT_OFFSET },
  { "load",        PAYLOAD_LOAD_ADDR_OFFSET   }
};

/**
  Parse the target firmware image info in FIT.
  @param[in]  Fdt            Memory address of a fdt.
  @param[in]  Firmware       Target name of an image.
  @param[out] Context        The FIT image context pointer.
  @retval EFI_NOT_FOUND      FIT node dose not find.
  @retval EFI_SUCCESS        FIT binary is loaded successfully.
**/
EFI_STATUS
EFIAPI
FitParseFirmwarePropertyData (
  IN   VOID               *Fdt,
  IN   CHAR8              *Firmware,
  OUT  FIT_IMAGE_CONTEXT  *Context
  )
{
  CONST FDT_PROPERTY  *PropertyPtr;
  INT32               ImageNode;
  INT32               TianoNode;
  INT32               TempLen;
  UINT32              *Data32;
  UINT64              *Data64;
  UINT32              *ContextOffset32;
  UINT64              *ContextOffset64;
  INT32               Index;

  ImageNode = FdtSubnodeOffsetNameLen (Fdt, 0, "images", (INT32)AsciiStrLen ("images"));
  if (ImageNode <= 0) {
    return EFI_NOT_FOUND;
  }

  TianoNode = FdtSubnodeOffsetNameLen (Fdt, ImageNode, Firmware, (INT32)AsciiStrLen (Firmware));
  if (TianoNode <= 0) {
    return EFI_NOT_FOUND;
  }

  for (Index = 0; Index < sizeof (PropertyData32List) / sizeof (PROPERTY_DATA); Index++) {
    PropertyPtr      = FdtGetProperty (Fdt, TianoNode, PropertyData32List[Index].Name, &TempLen);
    Data32           = (UINT32 *)(PropertyPtr->Data);
    ContextOffset32  = (UINT32 *)((UINTN)Context + PropertyData32List[Index].Offset);
    *ContextOffset32 = Fdt32ToCpu (*Data32);
  }

  for (Index = 0; Index < sizeof (PropertyData64List)/sizeof (PROPERTY_DATA); Index++) {
    PropertyPtr      = FdtGetProperty (Fdt, TianoNode, PropertyData64List[Index].Name, &TempLen);
    Data64           = (UINT64 *)(PropertyPtr->Data);
    ContextOffset64  = (UINT64 *)((UINTN)Context + PropertyData64List[Index].Offset);
    *ContextOffset64 = Fdt64ToCpu (*Data64);
  }

  return EFI_SUCCESS;
}

/**
  Parse the FIT image info.
  @param[in]  ImageBase      Memory address of an image.
  @param[out] Context        The FIT image context pointer.
  @retval EFI_UNSUPPORTED         Unsupported binary type.
  @retval EFI_SUCCESS             FIT binary is loaded successfully.
**/
EFI_STATUS
EFIAPI
ParseFitImage (
  IN   VOID               *ImageBase,
  OUT  FIT_IMAGE_CONTEXT  *Context
  )
{
  VOID                *Fdt;
  INT32               ConfigNode;
  INT32               Config1Node;
  CONST FDT_PROPERTY  *PropertyPtr;
  INT32               TempLen;
  UINT32              *Data32;
  UINT64              Value;
  EFI_STATUS          Status;
  UINTN               UplSize;
  CHAR8               *Firmware;

  Status = FdtCheckHeader (ImageBase);
  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  Fdt         = ImageBase;
  PropertyPtr = FdtGetProperty (Fdt, 0, "size", &TempLen);
  Data32      = (UINT32 *)(PropertyPtr->Data);
  UplSize     = Value = Fdt32ToCpu (*Data32);
  ConfigNode  = FdtSubnodeOffsetNameLen (Fdt, 0, "configurations", (INT32)AsciiStrLen ("configurations"));
  if (ConfigNode <= 0) {
    return EFI_NOT_FOUND;
  }

  Config1Node = FdtSubnodeOffsetNameLen (Fdt, ConfigNode, "conf-1", (INT32)AsciiStrLen ("conf-1"));
  if (Config1Node <= 0) {
    return EFI_NOT_FOUND;
  }

  PropertyPtr = FdtGetProperty (Fdt, Config1Node, "firmware", &TempLen);
  Firmware    = (CHAR8 *)(PropertyPtr->Data);

  FitParseFirmwarePropertyData (Fdt, Firmware, Context);

  Context->ImageBase          = (EFI_PHYSICAL_ADDRESS)ImageBase;
  Context->PayloadSize        = UplSize;
  Context->RelocateTableCount = (Context->PayloadEntrySize - (Context->RelocateTableOffset - Context->PayloadEntryOffset)) / sizeof (FIT_RELOCATE_ITEM);

  return EFI_SUCCESS;
}
