/** @file
  A hook-in library for MdeModulePkg/Universal/SmbiosDxe, in order to set
  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosVersion (and possibly other PCDs)
  just before SmbiosDxe consumes them.
  Copyright (C) 2013, 2015, Red Hat, Inc.
  Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include 
#include 
#include 
#include 
#include 
#include 
typedef union {
  SMBIOS_TABLE_ENTRY_POINT     V2;
  SMBIOS_TABLE_3_0_ENTRY_POINT V3;
} QEMU_SMBIOS_ANCHOR;
RETURN_STATUS
EFIAPI
DetectSmbiosVersion (
  VOID
  )
{
  FIRMWARE_CONFIG_ITEM Anchor, Tables;
  UINTN                AnchorSize, TablesSize;
  QEMU_SMBIOS_ANCHOR   QemuAnchor;
  UINT16               SmbiosVersion;
  RETURN_STATUS        PcdStatus;
  if (PcdGetBool (PcdQemuSmbiosValidated)) {
    //
    // Some other module, linked against this library, has already performed
    // the task at hand. This should never happen, but it's easy to handle;
    // just exit early.
    //
    return RETURN_SUCCESS;
  }
  if (RETURN_ERROR (QemuFwCfgFindFile (
                      "etc/smbios/smbios-anchor", &Anchor, &AnchorSize)) ||
      RETURN_ERROR (QemuFwCfgFindFile (
                      "etc/smbios/smbios-tables", &Tables, &TablesSize)) ||
      TablesSize == 0) {
    return RETURN_SUCCESS;
  }
  QemuFwCfgSelectItem (Anchor);
  switch (AnchorSize) {
  case sizeof QemuAnchor.V2:
    QemuFwCfgReadBytes (AnchorSize, &QemuAnchor);
    if (QemuAnchor.V2.MajorVersion != 2 ||
        QemuAnchor.V2.TableLength != TablesSize ||
        CompareMem (QemuAnchor.V2.AnchorString, "_SM_", 4) != 0 ||
        CompareMem (QemuAnchor.V2.IntermediateAnchorString, "_DMI_", 5) != 0) {
      return RETURN_SUCCESS;
    }
    SmbiosVersion = (UINT16)(QemuAnchor.V2.MajorVersion << 8 |
                             QemuAnchor.V2.MinorVersion);
    break;
  case sizeof QemuAnchor.V3:
    QemuFwCfgReadBytes (AnchorSize, &QemuAnchor);
    if (QemuAnchor.V3.MajorVersion != 3 ||
        QemuAnchor.V3.TableMaximumSize != TablesSize ||
        CompareMem (QemuAnchor.V3.AnchorString, "_SM3_", 5) != 0) {
      return RETURN_SUCCESS;
    }
    SmbiosVersion = (UINT16)(QemuAnchor.V3.MajorVersion << 8 |
                             QemuAnchor.V3.MinorVersion);
    DEBUG ((DEBUG_INFO, "%a: SMBIOS 3.x DocRev from QEMU: 0x%02x\n",
      __FUNCTION__, QemuAnchor.V3.DocRev));
    PcdStatus = PcdSet8S (PcdSmbiosDocRev, QemuAnchor.V3.DocRev);
    ASSERT_RETURN_ERROR (PcdStatus);
    break;
  default:
    return RETURN_SUCCESS;
  }
  DEBUG ((DEBUG_INFO, "%a: SMBIOS version from QEMU: 0x%04x\n", __FUNCTION__,
    SmbiosVersion));
  PcdStatus = PcdSet16S (PcdSmbiosVersion, SmbiosVersion);
  ASSERT_RETURN_ERROR (PcdStatus);
  //
  // SMBIOS platform drivers can now fetch and install
  // "etc/smbios/smbios-tables" from QEMU.
  //
  PcdStatus = PcdSetBoolS (PcdQemuSmbiosValidated, TRUE);
  ASSERT_RETURN_ERROR (PcdStatus);
  return RETURN_SUCCESS;
}