/** @file PCD PEIM

Copyright (c) 2006, Intel Corporation                                                         
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.             


Module Name: Pcd.c

**/

#include "Service.h"


PCD_PPI mPcdPpiInstance = {
  PeiPcdSetSku,

  PeiPcdGet8,
  PeiPcdGet16,          
  PeiPcdGet32,          
  PeiPcdGet64,          
  PeiPcdGetPtr,         
  PeiPcdGetBool,     
  PeiPcdGetSize,

  PeiPcdGet8Ex,
  PeiPcdGet16Ex,          
  PeiPcdGet32Ex,          
  PeiPcdGet64Ex,          
  PeiPcdGetPtrEx,         
  PeiPcdGetBoolEx,     
  PeiPcdGetSizeEx,
  
  PeiPcdSet8,
  PeiPcdSet16,          
  PeiPcdSet32,          
  PeiPcdSet64,          
  PeiPcdSetPtr,         
  PeiPcdSetBool,     

  PeiPcdSet8Ex,
  PeiPcdSet16Ex,          
  PeiPcdSet32Ex,          
  PeiPcdSet64Ex,          
  PeiPcdSetPtrEx,         
  PeiPcdSetBoolEx,

  PeiRegisterCallBackOnSet,
  PcdUnRegisterCallBackOnSet,
  PeiPcdGetNextToken,
  PeiPcdGetNextTokenSpace
};



STATIC EFI_PEI_PPI_DESCRIPTOR  mPpiPCD = {
  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
  &gPcdPpiGuid,
  &mPcdPpiInstance
};



EFI_STATUS
EFIAPI
PcdPeimInit (
  IN EFI_FFS_FILE_HEADER      *FfsHeader,
  IN EFI_PEI_SERVICES         **PeiServices
  )
{
  EFI_STATUS Status;

  BuildPcdDatabase ();
  
  Status = PeiServicesInstallPpi (&mPpiPCD);

  ASSERT_EFI_ERROR (Status);
  
  return EFI_SUCCESS;
}

VOID
EFIAPI
PeiPcdSetSku (
  IN  UINTN                  SkuId
  )
{

  GetPcdDatabase()->Init.SystemSkuId = (SKU_ID) SkuId;

  return;
}



UINT8
EFIAPI
PeiPcdGet8 (
  IN UINTN                    TokenNumber
  )
{
  return *((UINT8 *) GetWorker (TokenNumber, sizeof (UINT8)));
}



UINT16
EFIAPI
PeiPcdGet16 (
  IN UINTN                    TokenNumber
  )
{
  return ReadUnaligned16 (GetWorker (TokenNumber, sizeof (UINT16)));
}



UINT32
EFIAPI
PeiPcdGet32 (
  IN UINTN                    TokenNumber
  )
{
  return ReadUnaligned32 (GetWorker (TokenNumber, sizeof (UINT32)));
}



UINT64
EFIAPI
PeiPcdGet64 (
  IN UINTN                    TokenNumber
  )
{
  return ReadUnaligned64 (GetWorker (TokenNumber, sizeof (UINT64)));
}



VOID *
EFIAPI
PeiPcdGetPtr (
  IN UINTN                    TokenNumber
  )
{
  return GetWorker (TokenNumber, 0);
}



BOOLEAN
EFIAPI
PeiPcdGetBool (
  IN UINTN                    TokenNumber
  )
{
  return *((BOOLEAN *) GetWorker (TokenNumber, sizeof (BOOLEAN)));
}



UINTN
EFIAPI
PeiPcdGetSize (
  IN UINTN                    TokenNumber
  )
{
  PEI_PCD_DATABASE    *PeiPcdDb;
  UINTN               Size;
  UINTN               MaxSize;

  //
  // If DebugAssertEnabled is TRUE, we still need to provide the GET size
  // function as GetWorker and SetWoker need this function to do ASSERT.
  //
  if ((!FeaturePcdGet(PcdPeiPcdDatabaseGetSizeEnabled)) &&
      (!DebugAssertEnabled ())) {
    return 0;
  }

  PeiPcdDb = GetPcdDatabase ();
  //
  // TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER.
  // We have to decrement TokenNumber by 1 to make it usable
  // as the array index.
  //
  TokenNumber--;

  // EBC compiler is very choosy. It may report warning about comparison
  // between UINTN and 0 . So we add 1 in each size of the 
  // comparison.
  ASSERT (TokenNumber + 1 < PEI_LOCAL_TOKEN_NUMBER + 1);

  Size = (PeiPcdDb->Init.LocalTokenNumberTable[TokenNumber] & PCD_DATUM_TYPE_ALL_SET) >> PCD_DATUM_TYPE_SHIFT;

  if (Size == 0) {
    //
    // For pointer type, we need to scan the SIZE_TABLE to get the current size.
    //
    return GetPtrTypeSize (TokenNumber, &MaxSize, PeiPcdDb);
  } else {
    return Size;
  }

}



UINT8
EFIAPI
PeiPcdGet8Ex (
  IN CONST EFI_GUID             *Guid,
  IN UINTN                      ExTokenNumber
  )
{
  return *((UINT8 *) ExGetWorker (Guid, ExTokenNumber, sizeof (UINT8)));
}



UINT16
EFIAPI
PeiPcdGet16Ex (
  IN CONST EFI_GUID             *Guid,
  IN UINTN                      ExTokenNumber
  )
{
  return ReadUnaligned16 (ExGetWorker (Guid, ExTokenNumber, sizeof (UINT16)));
}



UINT32
EFIAPI
PeiPcdGet32Ex (
  IN CONST EFI_GUID             *Guid,
  IN UINTN                      ExTokenNumber
  )
{
  return ReadUnaligned32 (ExGetWorker (Guid, ExTokenNumber, sizeof (UINT32)));
}



UINT64
EFIAPI
PeiPcdGet64Ex (
  IN CONST EFI_GUID             *Guid,
  IN UINTN                      ExTokenNumber
  )
{
  return ReadUnaligned64 (ExGetWorker (Guid, ExTokenNumber, sizeof (UINT64)));
}



VOID *
EFIAPI
PeiPcdGetPtrEx (
  IN CONST EFI_GUID             *Guid,
  IN UINTN                      ExTokenNumber
  )
{
  return ExGetWorker (Guid, ExTokenNumber, 0);
}



BOOLEAN
EFIAPI
PeiPcdGetBoolEx (
  IN CONST  EFI_GUID              *Guid,
  IN UINTN                        ExTokenNumber
  )
{
  return *((BOOLEAN *) ExGetWorker (Guid, ExTokenNumber, sizeof (BOOLEAN)));
}



UINTN
EFIAPI
PeiPcdGetSizeEx (
  IN CONST  EFI_GUID              *Guid,
  IN UINTN                        ExTokenNumber
  )
{
  if ((!FeaturePcdGet (PcdPeiPcdDatabaseGetSizeEnabled)) ||  !FeaturePcdGet (PcdPeiPcdDatabaseExEnabled)) {
    return 0;
  }

  return PeiPcdGetSize (GetExPcdTokenNumber (Guid, ExTokenNumber));
}



EFI_STATUS
EFIAPI
PeiPcdSet8 (
  IN UINTN                        TokenNumber,
  IN UINT8                        Value
  )
{
  return SetValueWorker (TokenNumber, &Value, sizeof (Value));
}



EFI_STATUS
EFIAPI
PeiPcdSet16 (
  IN UINTN                         TokenNumber,
  IN UINT16                        Value
  )
{
  return SetValueWorker (TokenNumber, &Value, sizeof (Value));
}



EFI_STATUS
EFIAPI
PeiPcdSet32 (
  IN UINTN                         TokenNumber,
  IN UINT32                        Value
  )
{
  return SetValueWorker (TokenNumber, &Value, sizeof (Value));
}



EFI_STATUS
EFIAPI
PeiPcdSet64 (
  IN UINTN                         TokenNumber,
  IN UINT64                        Value
  )
{
  return SetValueWorker (TokenNumber, &Value, sizeof (Value));
}


EFI_STATUS
EFIAPI
PeiPcdSetPtr (
  IN      UINTN                    TokenNumber,
  IN OUT  UINTN                    *SizeOfBuffer,
  IN      VOID                     *Buffer
  )
{
  return SetWorker (TokenNumber, Buffer, SizeOfBuffer, TRUE);
}



EFI_STATUS
EFIAPI
PeiPcdSetBool (
  IN UINTN                         TokenNumber,
  IN BOOLEAN                       Value
  )
{
  return SetValueWorker (TokenNumber, &Value, sizeof (Value));
}



EFI_STATUS
EFIAPI
PeiPcdSet8Ex (
  IN CONST EFI_GUID               *Guid,
  IN UINTN                        ExTokenNumber,
  IN UINT8                        Value
  )
{
  return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
}



EFI_STATUS
EFIAPI
PeiPcdSet16Ex (
  IN CONST EFI_GUID               *Guid,
  IN UINTN                        ExTokenNumber,
  IN UINT16                       Value
  )
{
  return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
}



EFI_STATUS
EFIAPI
PeiPcdSet32Ex (
  IN CONST EFI_GUID               *Guid,
  IN UINTN                        ExTokenNumber,
  IN UINT32                       Value
  )
{
  return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
}



EFI_STATUS
EFIAPI
PeiPcdSet64Ex (
  IN CONST EFI_GUID               *Guid,
  IN UINTN                        ExTokenNumber,
  IN UINT64                       Value
  )
{
  return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
}



EFI_STATUS
EFIAPI
PeiPcdSetPtrEx (
  IN CONST EFI_GUID               *Guid,
  IN UINTN                        ExTokenNumber,
  IN UINTN                        *SizeOfBuffer,
  IN VOID                         *Value
  )
{
  return ExSetWorker (ExTokenNumber, Guid, Value, SizeOfBuffer, TRUE);
}



EFI_STATUS
EFIAPI
PeiPcdSetBoolEx (
  IN CONST EFI_GUID             *Guid,
  IN UINTN                      ExTokenNumber,
  IN BOOLEAN                    Value
  )
{
  return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
}




EFI_STATUS
EFIAPI
PeiRegisterCallBackOnSet (
  IN  CONST EFI_GUID              *Guid, OPTIONAL
  IN  UINTN                       ExTokenNumber,
  IN  PCD_PPI_CALLBACK            CallBackFunction
  )
{
  if (!FeaturePcdGet(PcdPeiPcdDatabaseCallbackOnSetEnabled)) {
    return EFI_UNSUPPORTED;
  }

  ASSERT (CallBackFunction != NULL);
  
  return PeiRegisterCallBackWorker (ExTokenNumber, Guid, CallBackFunction, TRUE);
}



EFI_STATUS
EFIAPI
PcdUnRegisterCallBackOnSet (
  IN  CONST EFI_GUID              *Guid, OPTIONAL
  IN  UINTN                       ExTokenNumber,
  IN  PCD_PPI_CALLBACK            CallBackFunction
  )
{
  if (!FeaturePcdGet(PcdPeiPcdDatabaseCallbackOnSetEnabled)) {
    return EFI_UNSUPPORTED;
  }

  ASSERT (CallBackFunction != NULL);
  
  return PeiRegisterCallBackWorker (ExTokenNumber, Guid, CallBackFunction, FALSE);
}



EFI_STATUS
EFIAPI
PeiPcdGetNextToken (
  IN CONST EFI_GUID               *Guid, OPTIONAL
  IN OUT  UINTN                   *TokenNumber
  )
{
  UINTN               GuidTableIdx;
  PEI_PCD_DATABASE    *PeiPcdDb;
  EFI_GUID            *MatchGuid;
  DYNAMICEX_MAPPING   *ExMapTable;
  UINTN               i;
  BOOLEAN             Found;
  BOOLEAN             PeiExMapTableEmpty;

  if (!FeaturePcdGet (PcdPeiPcdDatabaseTraverseEnabled)) {
    return EFI_UNSUPPORTED;
  }

  PeiExMapTableEmpty = PEI_EXMAP_TABLE_EMPTY;

  if (Guid == NULL) {
    if (*TokenNumber > PEI_NEX_TOKEN_NUMBER) {
      return EFI_NOT_FOUND;
    }
    (*TokenNumber)++;
    if (*TokenNumber > PEI_NEX_TOKEN_NUMBER) {
      *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
    }
    return EFI_SUCCESS;
  } else {
    if (PeiExMapTableEmpty) {
      *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
      return EFI_SUCCESS;
    }
    
    //
    // Assume PCD Database AutoGen tool is sorting the ExMap based on the following order
    // 1) ExGuid
    // 2) ExTokenNumber
    //
    PeiPcdDb = GetPcdDatabase ();
    
    MatchGuid = ScanGuid (PeiPcdDb->Init.GuidTable, sizeof(PeiPcdDb->Init.GuidTable), Guid);

    if (MatchGuid == NULL) {
      *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
      return EFI_NOT_FOUND;
    }

    GuidTableIdx = MatchGuid - PeiPcdDb->Init.GuidTable;

    ExMapTable = PeiPcdDb->Init.ExMapTable;

    Found = FALSE;
    //
    // Locate the GUID in ExMapTable first.
    //
    for (i = 0; i < PEI_EXMAPPING_TABLE_SIZE; i++) {
      if (ExMapTable[i].ExGuidIndex == GuidTableIdx) {
        Found = TRUE;
        break;
      }
    }

    if (Found) {
      if (*TokenNumber == PCD_INVALID_TOKEN_NUMBER) {
        *TokenNumber = ExMapTable[i].ExTokenNumber;
         return EFI_SUCCESS;
      }

      for ( ; i < PEI_EXMAPPING_TABLE_SIZE; i++) {
        if (ExMapTable[i].ExTokenNumber == *TokenNumber) {
          i++;
          if (i == PEI_EXMAPPING_TABLE_SIZE) {
            //
            // Exceed the length of ExMap Table
            //
            *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
            return EFI_SUCCESS;
          }
          if (ExMapTable[i].ExGuidIndex == GuidTableIdx) {
            *TokenNumber = ExMapTable[i].ExTokenNumber;
            return EFI_SUCCESS;
          } else {
            *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
            return EFI_SUCCESS;
          }
        }
      }
      return EFI_NOT_FOUND;
    }
  }

  return EFI_NOT_FOUND;
}



EFI_STATUS
EFIAPI
PeiPcdGetNextTokenSpace (
  IN OUT CONST EFI_GUID          **Guid
  )
{
  UINTN               GuidTableIdx;
  EFI_GUID            *MatchGuid;
  PEI_PCD_DATABASE    *PeiPcdDb;
  DYNAMICEX_MAPPING   *ExMapTable;
  UINTN               i;
  BOOLEAN             Found;
  BOOLEAN             PeiExMapTableEmpty;

  if (!FeaturePcdGet (PcdPeiPcdDatabaseTraverseEnabled)) {
    return EFI_UNSUPPORTED;
  }

  ASSERT (Guid != NULL);

  PeiExMapTableEmpty = PEI_EXMAP_TABLE_EMPTY;

  if (PeiExMapTableEmpty) {
    if (*Guid != NULL) {
      return EFI_NOT_FOUND;
    } else {
      return EFI_SUCCESS;
    }
  }

  //
  // Assume PCD Database AutoGen tool is sorting the ExMap based on the following order
  // 1) ExGuid
  // 2) ExTokenNumber
  //
  PeiPcdDb = GetPcdDatabase ();

  ExMapTable = PeiPcdDb->Init.ExMapTable;

  if (*Guid == NULL) {
    //
    // return the first Token Space Guid.
    //
    *Guid = &PeiPcdDb->Init.GuidTable[ExMapTable[0].ExGuidIndex];
    return EFI_SUCCESS;
  }

  MatchGuid = ScanGuid (PeiPcdDb->Init.GuidTable, sizeof(PeiPcdDb->Init.GuidTable), *Guid);

  if (MatchGuid == NULL) {
    return EFI_NOT_FOUND;
  }
  
  GuidTableIdx = MatchGuid - PeiPcdDb->Init.GuidTable;

  Found = FALSE;
  for (i = 0; i < PEI_EXMAPPING_TABLE_SIZE; i++) {
    if (ExMapTable[i].ExGuidIndex == GuidTableIdx) {
      Found = TRUE;
      break;
    }
  }

  if (Found) {
    i++;
    for ( ; i < PEI_EXMAPPING_TABLE_SIZE; i++ ) {
      if (ExMapTable[i].ExGuidIndex != GuidTableIdx ) {
        *Guid = &PeiPcdDb->Init.GuidTable[ExMapTable[i].ExGuidIndex];
        return EFI_SUCCESS;
      }
    }
    *Guid = NULL;
    return EFI_SUCCESS;
  }

  return EFI_NOT_FOUND;

}

UINTN
GetPtrTypeSize (
  IN    UINTN             LocalTokenNumberTableIdx,
  OUT   UINTN             *MaxSize,
  IN    PEI_PCD_DATABASE  *Database
  )
{
  INTN        SizeTableIdx;
  UINTN       LocalTokenNumber;
  SKU_ID      *SkuIdTable;
  SIZE_INFO   *SizeTable;
  UINTN       i;

  SizeTableIdx = GetSizeTableIndex (LocalTokenNumberTableIdx, Database);

  LocalTokenNumber = Database->Init.LocalTokenNumberTable[LocalTokenNumberTableIdx];

  ASSERT ((LocalTokenNumber & PCD_DATUM_TYPE_ALL_SET) == PCD_DATUM_TYPE_POINTER);
  
  SizeTable = Database->Init.SizeTable;

  *MaxSize = SizeTable[SizeTableIdx];
  //
  // SizeTable only contain record for PCD_DATUM_TYPE_POINTER type 
  // PCD entry.
  //
  if (LocalTokenNumber & PCD_TYPE_VPD) {
      //
      // We have only one entry for VPD enabled PCD entry:
      // 1) MAX Size.
      // We consider current size is equal to MAX size.
      //
      return *MaxSize;
  } else {
    if ((LocalTokenNumber & PCD_TYPE_SKU_ENABLED) == 0) {
      //
      // We have only two entry for Non-Sku enabled PCD entry:
      // 1) MAX SIZE
      // 2) Current Size
      //
      return SizeTable[SizeTableIdx + 1];
    } else {
      //
      // We have these entry for SKU enabled PCD entry
      // 1) MAX SIZE
      // 2) Current Size for each SKU_ID (It is equal to MaxSku).
      //
      SkuIdTable = GetSkuIdArray (LocalTokenNumberTableIdx, Database);
      for (i = 0; i < SkuIdTable[0]; i++) {
        if (SkuIdTable[1 + i] == Database->Init.SystemSkuId) {
          return SizeTable[SizeTableIdx + 1 + i];
        }
      }
      return SizeTable[SizeTableIdx + 1];
    }
  }
}



BOOLEAN
SetPtrTypeSize (
  IN          UINTN             LocalTokenNumberTableIdx,
  IN    OUT   UINTN             *CurrentSize,
  IN          PEI_PCD_DATABASE  *Database
  )
{
  INTN        SizeTableIdx;
  UINTN       LocalTokenNumber;
  SKU_ID      *SkuIdTable;
  SIZE_INFO   *SizeTable;
  UINTN       i;
  UINTN       MaxSize;
  
  SizeTableIdx = GetSizeTableIndex (LocalTokenNumberTableIdx, Database);

  LocalTokenNumber = Database->Init.LocalTokenNumberTable[LocalTokenNumberTableIdx];

  ASSERT ((LocalTokenNumber & PCD_DATUM_TYPE_ALL_SET) == PCD_DATUM_TYPE_POINTER);
  
  SizeTable = Database->Init.SizeTable;

  MaxSize = SizeTable[SizeTableIdx];
  //
  // SizeTable only contain record for PCD_DATUM_TYPE_POINTER type 
  // PCD entry.
  //
  if (LocalTokenNumber & PCD_TYPE_VPD) {
      //
      // We shouldn't come here as we don't support SET for VPD
      //
      ASSERT (FALSE);
      return FALSE;
  } else {
    if ((*CurrentSize > MaxSize) ||
      (*CurrentSize == MAX_ADDRESS)) {
       *CurrentSize = MaxSize;
       return FALSE;
    } 
    
    if ((LocalTokenNumber & PCD_TYPE_SKU_ENABLED) == 0) {
      //
      // We have only two entry for Non-Sku enabled PCD entry:
      // 1) MAX SIZE
      // 2) Current Size
      //
      SizeTable[SizeTableIdx + 1] = (SIZE_INFO) *CurrentSize;
      return TRUE;
    } else {
      //
      // We have these entry for SKU enabled PCD entry
      // 1) MAX SIZE
      // 2) Current Size for each SKU_ID (It is equal to MaxSku).
      //
      SkuIdTable = GetSkuIdArray (LocalTokenNumberTableIdx, Database);
      for (i = 0; i < SkuIdTable[0]; i++) {
        if (SkuIdTable[1 + i] == Database->Init.SystemSkuId) {
          SizeTable[SizeTableIdx + 1 + i] = (SIZE_INFO) *CurrentSize;
          return TRUE;
        }
      }
      SizeTable[SizeTableIdx + 1] = (SIZE_INFO) *CurrentSize;
      return TRUE;
    }
  }

}
