render / rpms / edk2

Forked from rpms/edk2 3 months ago
Clone
Blob Blame History Raw
From 0e182f2305a84fdf62ff2631de6e363a5a881287 Mon Sep 17 00:00:00 2001
From: Laszlo Ersek <lersek@redhat.com>
Date: Wed, 5 Jun 2013 10:14:34 +0200
Subject: [PATCH 1/3] OvmfPkg/SmbiosPlatformDxe: install legacy QEMU tables and
 save fields (X86)

Introduce basic legacy SMBIOS machinery for the QEMU platform:
- Install SMBIOS tables that QEMU passes down in complete form via fw_cfg.
- Stash individual fields that QEMU passes down to override the boot
  firmware's default SMBIOS tables.
- Add helper functions that OVMF's default SMBIOS tables will need.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
 OvmfPkg/SmbiosPlatformDxe/QemuLegacy.c          | 694 ++++++++++++++++++++++++
 OvmfPkg/SmbiosPlatformDxe/QemuLegacy.h          |  52 ++
 OvmfPkg/SmbiosPlatformDxe/QemuLegacyInternal.h  | 221 ++++++++
 OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.c   |  17 +-
 OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf |   2 +
 5 files changed, 983 insertions(+), 3 deletions(-)
 create mode 100644 OvmfPkg/SmbiosPlatformDxe/QemuLegacy.c
 create mode 100644 OvmfPkg/SmbiosPlatformDxe/QemuLegacy.h
 create mode 100644 OvmfPkg/SmbiosPlatformDxe/QemuLegacyInternal.h

diff --git a/OvmfPkg/SmbiosPlatformDxe/QemuLegacy.c b/OvmfPkg/SmbiosPlatformDxe/QemuLegacy.c
new file mode 100644
index 0000000..9c57558
--- /dev/null
+++ b/OvmfPkg/SmbiosPlatformDxe/QemuLegacy.c
@@ -0,0 +1,694 @@
+/** @file
+  This file fetches and installs SMBIOS tables on the QEMU hypervisor.
+
+  Copyright (C) 2013, Red Hat, Inc.
+
+  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.
+**/
+
+#include <Library/MemoryAllocationLib.h>
+
+#include "QemuLegacy.h"
+#include "QemuLegacyInternal.h"
+
+
+//
+// An SMBIOS entry exported by QEMU over fw_cfg can have one of the following
+// types.
+//
+typedef enum
+{
+  ET_FIELD, // defines one field in some SMBIOS table
+  ET_TABLE  // defines an SMBIOS table instance in entirety
+} FW_CFG_SMBIOS_ENTRY_TYPE;
+
+//
+// Header type introducing each entry in the QemuFwCfgItemX86SmbiosTables
+// fw_cfg blob.
+//
+#pragma pack(1)
+typedef struct {
+  UINT16 Size; // including payload and this header
+  UINT8  Type; // value from FW_CFG_SMBIOS_ENTRY_TYPE
+} FW_CFG_SMBIOS_ENTRY_HDR;
+#pragma pack()
+
+//
+// Fields included at the beginning of the the payload in QEMU SMBIOS entries
+// with ET_FIELD type.
+//
+#pragma pack(1)
+typedef struct {
+  UINT8  TableType; // SMBIOS table type to patch
+  UINT16 Offset;    // offset of a field in the formatted area
+} FW_CFG_SMBIOS_FIELD;
+#pragma pack()
+
+
+/**
+  Initialize a context object tracking SMBIOS table installation and patches
+  for fields.
+
+  @param[out] Context  A BUILD_CONTEXT object allocated dynamically and
+                       initialized.
+
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+  @retval EFI_SUCCESS           Allocation and initialization successful.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+InitSmbiosContext (
+  OUT BUILD_CONTEXT **Context
+  )
+{
+  *Context = AllocateZeroPool (sizeof **Context);
+  if (*Context == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: out of memory\n", __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Release a context object tracking SMBIOS table installation and patches for
+  fields.
+
+  @param[in,out] Context  The BUILD_CONTEXT object to tear down.
+**/
+STATIC
+VOID
+EFIAPI
+UninitSmbiosContext (
+  IN OUT BUILD_CONTEXT *Context
+  )
+{
+  INT32 Type;
+  INT32 Idx;
+
+  //
+  // free all patches
+  //
+  for (Type = 0; Type < TABLE_TYPE_LIMIT; ++Type) {
+    for (Idx = 0; Idx < PATCH_SUBSCRIPT_LIMIT; ++Idx) {
+      PATCH *Patch;
+
+      Patch = &Context->Table[Type].Patch[Idx];
+      if (Patch->Base != NULL) {
+        FreePool (Patch->Base);
+      }
+    }
+  }
+  FreePool (Context);
+}
+
+
+/**
+  Save a patch targeting an SMBIOS field in dynamically allocated memory.
+
+  @param[in,out] Context      The initialized BUILD_CONTEXT object to save the
+                              patch in.
+  @param[in]     TableType    The patch to be saved targets this table type.
+                              Patches for table types equal to or greater than
+                              TABLE_TYPE_LIMIT are ignored.
+  @param[in]     FieldOffset  The patch to be saved targets the field that
+                              begins at offset FieldOffset in SMBIOS table type
+                              TableType. FieldOffset is enforced not to point
+                              into the SMBIOS table header. A FieldOffset value
+                              equal to or greater than 255 is rejected, since
+                              the formatted area of an SMBIOS table never
+                              exceeds 255 bytes. FieldOffset is not validated
+                              against actual field offsets here, it is only
+                              saved for later lookup.
+  @param[in]     PatchData    Byte array constituting the patch body.
+  @param[in]     PatchSize    Number of bytes in PatchData.
+
+  @retval EFI_SUCCESS            Patch has been either ignored due to not
+                                 meeting the criterion on TableType, or it has
+                                 been saved successfully.
+  @retval EFI_INVALID_PARAMETER  FieldOffset is invalid.
+  @retval EFI_OUT_OF_RESOURCES   Couldn't allocate memory for the patch.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SaveSmbiosPatch (
+  IN OUT BUILD_CONTEXT *Context,
+  IN     UINT8         TableType,
+  IN     UINT16        FieldOffset,
+  IN     UINT8         *PatchData,
+  IN     UINT16        PatchSize
+)
+{
+  UINT8 *NewBase;
+  PATCH *Patch;
+
+  if (TableType >= TABLE_TYPE_LIMIT) {
+    DEBUG ((DEBUG_VERBOSE,
+      "%a: ignoring patch for unsupported table type %d\n",
+      __FUNCTION__, TableType));
+    return EFI_SUCCESS;
+  }
+
+  if (FieldOffset < FIELD_OFFSET_MINIMUM
+      || FieldOffset - FIELD_OFFSET_MINIMUM >= PATCH_SUBSCRIPT_LIMIT) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: invalid patch for table type %d field offset %d\n",
+      __FUNCTION__, TableType, FieldOffset));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  NewBase = AllocateCopyPool (PatchSize, PatchData);
+  if (PatchSize > 0 && NewBase == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: table type %d field offset %d: out of memory\n",
+      __FUNCTION__, TableType, FieldOffset));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Patch = &Context->Table[TableType].Patch[FieldOffset - FIELD_OFFSET_MINIMUM];
+  //
+  // replace previous patch if it exists
+  //
+  if (Patch->Base != NULL) {
+    DEBUG ((DEBUG_VERBOSE,
+      "%a: replacing prior patch for table type %d field offset %d\n",
+      __FUNCTION__, TableType, FieldOffset));
+    FreePool (Patch->Base);
+  }
+
+  Patch->Base = NewBase;
+  Patch->Size = PatchSize;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Apply a saved patch to a field located in the formatted are of a not yet
+  installed SMBIOS table.
+
+  The patch is looked up based on (Context, TableType, FieldOffset).
+
+  @param[in]  Context      The BUILD_CONTEXT object storing saved patches.
+  @param[in]  TableType    Selects the table type for which the patch has been
+                           saved. It is assumed that the caller has validated
+                           TableType against TABLE_TYPE_LIMIT (upper
+                           exclusive).
+  @param[in]  FieldOffset  Selects the SMBIOS field for which the patch has
+                           been saved. It is assumed that the caller has
+                           validated FieldOffset against FIELD_OFFSET_MINIMUM
+                           (lower inclusive) and 255 (upper exclusive).
+  @param[in]  FieldSize    The caller supplies the size of the field to patch
+                           in FieldSize. The patch saved for
+                           TableType:FieldOffset, if any, is only applied if
+                           its size equals FieldSize.
+  @param[out] TableBase    Base of the SMBIOS table of type TableType in which
+                           the field starting at FieldOffset needs to be
+                           patched.
+
+  @retval EFI_NOT_FOUND          No patch found for TableType:FieldOffset in
+                                 Context. This return value is considered
+                                 informative (ie. non-fatal).
+  @retval EFI_INVALID_PARAMETER  Patch found for TableType:FieldOffset, but its
+                                 size doesn't match FieldSize. This result is
+                                 considered a fatal error of the patch origin.
+  @retval EFI_SUCCESS            The SMBIOS table at TableBase has been patched
+                                 starting at FieldOffset for a length of
+                                 FieldSize.
+**/
+EFI_STATUS
+EFIAPI
+PatchSmbiosFormatted (
+  IN  BUILD_CONTEXT *Context,
+  IN  UINT8         TableType,
+  IN  UINT16        FieldOffset,
+  IN  UINT16        FieldSize,
+  OUT UINT8         *TableBase
+  )
+{
+  PATCH *Patch;
+
+  ASSERT (TableType < TABLE_TYPE_LIMIT);
+  ASSERT (FieldOffset >= FIELD_OFFSET_MINIMUM);
+  ASSERT (FieldOffset - FIELD_OFFSET_MINIMUM < PATCH_SUBSCRIPT_LIMIT);
+
+  Patch = &Context->Table[TableType].Patch[FieldOffset - FIELD_OFFSET_MINIMUM];
+  if (Patch->Base == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (Patch->Size != FieldSize) {
+    DEBUG ((DEBUG_ERROR, "%a: table type %d, field offset %d: "
+      "patch size %d doesn't match field size %d\n",
+      __FUNCTION__, TableType, FieldOffset, Patch->Size, FieldSize));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CopyMem (TableBase + FieldOffset, Patch->Base, FieldSize);
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Apply a saved patch to a text string located in the unformatted area of an
+  already installed SMBIOS table.
+
+  The patch is looked up based on (Context, TableType, FieldOffset).
+
+  @param[in]  Smbios        The EFI_SMBIOS_PROTOCOL instance used previously
+                            for installing the SMBIOS table.
+  @param[in]  SmbiosHandle  The EFI_SMBIOS_HANDLE previously returned by
+                            Smbios->Add().
+  @param[in]  Context       The BUILD_CONTEXT object storing saved patches.
+  @param[in]  TableType     Selects the table type for which the patch has been
+                            saved. It is assumed that the caller has validated
+                            TableType against TABLE_TYPE_LIMIT (upper
+                            exclusive).
+  @param[in]  FieldOffset   Selects the SMBIOS field for which the patch has
+                            been saved. It is assumed that the caller has
+                            validated FieldOffset against FIELD_OFFSET_MINIMUM
+                            (lower inclusive) and 255 (upper exclusive).
+                            It is also assumed that TableBase[FieldOffset]
+                            accesses a field of type SMBIOS_TABLE_STRING, ie. a
+                            field in the formatted area that identifies an
+                            existent text string in the unformatted area. Text
+                            string identifiers are one-based.
+  @param[out] TableBase     Base of the SMBIOS table of type TableType in which
+                            the SMBIOS_TABLE_STRING field at FieldOffset
+                            identifies the existent text string to update.
+
+  @retval EFI_NOT_FOUND          No patch found for TableType:FieldOffset in
+                                 Context. This return value is considered
+                                 informative (ie. non-fatal).
+  @retval EFI_INVALID_PARAMETER  Patch found for TableType:FieldOffset, but it
+                                 doesn't end with a NUL character. This result
+                                 is considered a fatal error of the patch
+                                 origin.
+  @retval EFI_SUCCESS            The text string identified by
+                                 TableBase[FieldOffset] has been replaced in
+                                 the installed SMBIOS table under SmbiosHandle.
+  @return                        Error codes returned by
+                                 Smbios->UpdateString(). EFI_NOT_FOUND shall
+                                 not be returned.
+**/
+EFI_STATUS
+EFIAPI
+PatchSmbiosUnformatted (
+  IN  EFI_SMBIOS_PROTOCOL *Smbios,
+  IN  EFI_SMBIOS_HANDLE   SmbiosHandle,
+  IN  BUILD_CONTEXT       *Context,
+  IN  UINT8               TableType,
+  IN  UINT16              FieldOffset,
+  IN  UINT8               *TableBase
+  )
+{
+  PATCH      *Patch;
+  UINTN      StringNumber;
+  EFI_STATUS Status;
+
+  ASSERT (TableType < TABLE_TYPE_LIMIT);
+  ASSERT (FieldOffset >= FIELD_OFFSET_MINIMUM);
+  ASSERT (FieldOffset - FIELD_OFFSET_MINIMUM < PATCH_SUBSCRIPT_LIMIT);
+
+  Patch = &Context->Table[TableType].Patch[FieldOffset - FIELD_OFFSET_MINIMUM];
+  if (Patch->Base == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (Patch->Size == 0 || Patch->Base[Patch->Size - 1] != '\0') {
+    DEBUG ((DEBUG_ERROR, "%a: table type %d, field offset %d: "
+      "missing terminator, or trailing garbage\n",
+      __FUNCTION__, TableType, FieldOffset));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  StringNumber = TableBase[FieldOffset];
+  ASSERT (StringNumber != 0);
+
+  Status = Smbios->UpdateString (Smbios, &SmbiosHandle, &StringNumber,
+                     (CHAR8 *)Patch->Base);
+  if (EFI_ERROR (Status)) {
+    ASSERT (Status != EFI_NOT_FOUND);
+    DEBUG ((DEBUG_ERROR, "%a: table type %d, field offset %d, "
+      "string number %d: Smbios->UpdateString(): %r\n", __FUNCTION__,
+      TableType, FieldOffset, TableBase[FieldOffset], Status));
+    return Status;
+  }
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Process an SMBIOS firmware configuration entry with ET_FIELD type, exported
+  by QEMU under QemuFwCfgItemX86SmbiosTables.
+
+  Such entries describe patches to be saved with SaveSmbiosPatch().
+
+  @param[in,out] Context      The BUILD_CONTEXT object tracking saved patches.
+  @param[in]     Payload      Points to the buffer to parse as
+                              FW_CFG_SMBIOS_FIELD.
+  @param[in]     PayloadSize  Number of bytes in Payload.
+
+  @retval EFI_INVALID_PARAMETER  PayloadSize is less than the size of
+                                 FW_CFG_SMBIOS_FIELD -- fields describing
+                                 the patch are incomplete.
+  @retval EFI_SUCCESS            Payload has been parsed and patch has been
+                                 saved successfully.
+  @return                        Error codes returned by SaveSmbiosPatch().
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VisitSmbiosField (
+  IN OUT BUILD_CONTEXT *Context,
+  IN     UINT8         *Payload,
+  IN     UINT16        PayloadSize
+  )
+{
+  FW_CFG_SMBIOS_FIELD *Field;
+
+  if (PayloadSize < (INT32) sizeof *Field) {
+    DEBUG ((DEBUG_ERROR, "%a: required minimum size %d, available %d\n",
+      __FUNCTION__, (INT32) sizeof *Field, PayloadSize));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Field = (FW_CFG_SMBIOS_FIELD *) Payload;
+  return SaveSmbiosPatch (Context, Field->TableType, Field->Offset,
+           Payload + sizeof *Field, (UINT16) (PayloadSize - sizeof *Field));
+}
+
+
+/**
+  Process an SMBIOS firmware configuration entry with ET_TABLE type, exported
+  by QEMU under QemuFwCfgItemX86SmbiosTables.
+
+  Such entries describe entire SMBIOS table instances to install verbatim. This
+  module never overrides tables installed in this manner with default tables.
+
+  @param[in]     Smbios          The EFI_SMBIOS_PROTOCOL instance used for
+                                 installing SMBIOS tables.
+  @param[in]     ProducerHandle  Passed on to Smbios->Add(), ProducerHandle
+                                 tracks the origin of installed SMBIOS tables.
+  @param[in,out] Context         The BUILD_CONTEXT object tracking installed
+                                 tables.
+  @param[in]     Payload         Points to the buffer to install as an SMBIOS
+                                 table.
+  @param[in]     PayloadSize     Number of bytes in Payload.
+
+  @retval EFI_INVALID_PARAMETER  The buffer at Payload, interpreted as an
+                                 SMBIOS table, failed basic sanity checks.
+  @retval EFI_SUCCESS            Payload has been installed successfully as an
+                                 SMBIOS table.
+  @return                        Error codes returned by Smbios->Add().
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VisitSmbiosTable (
+  IN     EFI_SMBIOS_PROTOCOL *Smbios,
+  IN     EFI_HANDLE          ProducerHandle,
+  IN OUT BUILD_CONTEXT       *Context,
+  IN     UINT8               *Payload,
+  IN     UINT16              PayloadSize
+  )
+{
+  SMBIOS_STRUCTURE  *SmbiosHeader;
+  UINT16            MinimumSize;
+  EFI_SMBIOS_HANDLE SmbiosHandle;
+  EFI_STATUS        Status;
+
+  //
+  // Basic sanity checks only in order to help debugging and to catch blatantly
+  // invalid data passed with "-smbios file=binary_file" on the QEMU command
+  // line. Beyond these we don't enforce correct, type-specific SMBIOS table
+  // formatting.
+  //
+  if (PayloadSize < (INT32) sizeof *SmbiosHeader) {
+    DEBUG ((DEBUG_ERROR, "%a: required minimum size %d, available %d\n",
+      __FUNCTION__, (INT32) sizeof *SmbiosHeader, PayloadSize));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SmbiosHeader = (SMBIOS_STRUCTURE *) Payload;
+
+  if (SmbiosHeader->Length < (INT32) sizeof *SmbiosHeader) {
+    DEBUG ((DEBUG_ERROR, "%a: required minimum size %d, stated %d\n",
+      __FUNCTION__, (INT32) sizeof *SmbiosHeader, SmbiosHeader->Length));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  MinimumSize = (UINT16) (SmbiosHeader->Length + 2);
+
+  if (PayloadSize < MinimumSize) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: minimum for formatted area plus terminator is %d, available %d\n",
+      __FUNCTION__, MinimumSize, PayloadSize));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Payload[PayloadSize - 2] != '\0' ||
+      Payload[PayloadSize - 1] != '\0') {
+    DEBUG ((DEBUG_ERROR, "%a: missing terminator, or trailing garbage\n",
+      __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // request unique handle
+  //
+  SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
+  Status = Smbios->Add (Smbios, ProducerHandle, &SmbiosHandle,
+                     (EFI_SMBIOS_TABLE_HEADER *) SmbiosHeader);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Smbios->Add(): %r\n", __FUNCTION__, Status));
+    return Status;
+  }
+
+  //
+  // track known tables
+  //
+  if (SmbiosHeader->Type < TABLE_TYPE_LIMIT) {
+    Context->Table[SmbiosHeader->Type].Installed = TRUE;
+  }
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Traverse the SMBIOS firmware configuration blob exported by QEMU under
+  QemuFwCfgItemX86SmbiosTables, processing each entry in turn.
+
+  Entries with ET_FIELD type are parsed as patches for the SMBIOS tables this
+  module installs as fallbacks, while entries of type ET_TABLE are parsed and
+  installed as verbatim SMBIOS tables.
+
+  Unknown entry types are silently skipped. Any error encountered during
+  traversal (for example, a recognized but malformed entry) aborts the
+  iteration, leaving the function with a possibly incomplete set of installed
+  tables.
+
+  @param[in]     Smbios          The EFI_SMBIOS_PROTOCOL instance used for
+                                 installing SMBIOS tables.
+  @param[in]     ProducerHandle  Passed on to Smbios->Add(), ProducerHandle
+                                 tracks the origin of installed SMBIOS tables.
+  @param[in,out] Context         The BUILD_CONTEXT object tracking installed
+                                 tables and saved patches.
+
+  @retval EFI_SUCCESS            The firmware configuration interface is
+                                 unavailable (no patches saved, no tables
+                                 installed).
+  @retval EFI_SUCCESS            Traversal complete. Tables provided by QEMU
+                                 have been installed. Patches have been saved
+                                 for any default tables that will be necessary.
+  @retval EFI_INVALID_PARAMETER  Encountered a corrupt entry in the SMBIOS
+                                 firmware configuration blob.
+  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
+  @return                        Error codes returned by VisitSmbiosField() and
+                                 VisitSmbiosTable().
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ScanQemuSmbios (
+  IN     EFI_SMBIOS_PROTOCOL *Smbios,
+  IN     EFI_HANDLE          ProducerHandle,
+  IN OUT BUILD_CONTEXT       *Context
+  )
+{
+  EFI_STATUS Status;
+  UINT16     NumEntries;
+  UINT16     CurEntry;
+
+  Status = EFI_SUCCESS;
+
+  if (!QemuFwCfgIsAvailable ()) {
+    return Status;
+  }
+
+  QemuFwCfgSelectItem (QemuFwCfgItemX86SmbiosTables);
+
+  NumEntries = QemuFwCfgRead16 ();
+  for (CurEntry = 0; CurEntry < NumEntries && !EFI_ERROR (Status);
+       ++CurEntry) {
+    FW_CFG_SMBIOS_ENTRY_HDR Header;
+    UINT16                  PayloadSize;
+    UINT8                   *Payload;
+
+    QemuFwCfgReadBytes (sizeof Header, &Header);
+
+    if (Header.Size < (INT32) sizeof Header) {
+      DEBUG ((DEBUG_ERROR, "%a: invalid header size %d in entry %d\n",
+        __FUNCTION__, Header.Size, CurEntry));
+      return EFI_INVALID_PARAMETER;
+    }
+
+    PayloadSize = (UINT16) (Header.Size - sizeof Header);
+    Payload = AllocatePool (PayloadSize);
+
+    if (PayloadSize > 0 && Payload == NULL) {
+      DEBUG ((DEBUG_ERROR, "%a: failed to allocate %d bytes for entry %d\n",
+        __FUNCTION__, PayloadSize, CurEntry));
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    QemuFwCfgReadBytes (PayloadSize, Payload);
+
+    //
+    // dump the payload
+    //
+    DEBUG_CODE (
+      UINT16 Idx;
+
+      DEBUG ((DEBUG_VERBOSE,
+        "%a: entry %d, type %d, payload size %d, payload hex dump follows:",
+        __FUNCTION__, CurEntry, Header.Type, PayloadSize));
+      for (Idx = 0; Idx < PayloadSize; ++Idx) {
+        switch (Idx % 16) {
+          case 0:
+            DEBUG ((DEBUG_VERBOSE, "\n%04X:", Idx));
+            break;
+          case 8:
+            DEBUG ((DEBUG_VERBOSE, " "));
+            break;
+          default:
+            ;
+        }
+        DEBUG ((DEBUG_VERBOSE, " %02X", Payload[Idx]));
+      }
+      DEBUG ((DEBUG_VERBOSE, "\n"));
+    );
+
+    switch (Header.Type) {
+    case ET_FIELD:
+      Status = VisitSmbiosField (Context, Payload, PayloadSize);
+      break;
+    case ET_TABLE:
+      Status = VisitSmbiosTable (Smbios, ProducerHandle, Context, Payload,
+                 PayloadSize);
+      break;
+    default:
+      ;
+    }
+
+    FreePool (Payload);
+  }
+
+  return Status;
+}
+
+
+/**
+  Install some of the default SMBIOS tables for table types that QEMU hasn't
+  provided under QemuFwCfgItemX86SmbiosTables, but are required by the
+  SMBIOS-2.7.1 specification.
+
+  @param[in]     Smbios          The EFI_SMBIOS_PROTOCOL instance used for
+                                 installing SMBIOS tables.
+  @param[in]     ProducerHandle  Passed on to Smbios->Add(), ProducerHandle
+                                 tracks the origin of installed SMBIOS tables.
+  @param[in,out] Context         The BUILD_CONTEXT object tracking installed
+                                 tables and saved patches.
+
+  @return  Status codes returned by the InstallSmbiosTypeXX() functions,
+           including the final EFI_SUCCESS if all such calls succeed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+InstallDefaultTables (
+  IN     EFI_SMBIOS_PROTOCOL *Smbios,
+  IN     EFI_HANDLE          ProducerHandle,
+  IN OUT BUILD_CONTEXT       *Context
+  )
+{
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Fetch and install SMBIOS tables on the QEMU hypervisor.
+
+  First, tables provided by QEMU in entirety are installed verbatim.
+
+  Then the function prepares some of the remaining tables required by the
+  SMBIOS-2.7.1 specification. For each such table,
+  - if QEMU provides any fields for the table, they take effect verbatim,
+  - remaining fields are set by this function.
+
+  @param[in] Smbios       The EFI_SMBIOS_PROTOCOL instance used for installing
+                          the SMBIOS tables.
+  @param[in] ImageHandle  The image handle of the calling module, passed as
+                          ProducerHandle to the Smbios->Add() call.
+
+  @retval EFI_SUCCESS            All tables have been installed.
+  @retval EFI_UNSUPPORTED        The pair (Smbios->MajorVersion,
+                                 Smbios->MinorVersion) precedes (2, 3)
+                                 lexicographically.
+  @return                        Error codes returned by Smbios->Add() or
+                                 internal functions. Some tables may not have
+                                 been installed or fully patched.
+**/
+EFI_STATUS
+EFIAPI
+InstallQemuSmbiosTables (
+  IN EFI_SMBIOS_PROTOCOL *Smbios,
+  IN EFI_HANDLE          ImageHandle
+  )
+{
+  EFI_STATUS    Status;
+  BUILD_CONTEXT *Context;
+
+  if (Smbios->MajorVersion < 2 || Smbios->MinorVersion < 3) {
+    DEBUG ((DEBUG_ERROR, "%a: unsupported Smbios version %d.%d\n",
+      __FUNCTION__, Smbios->MajorVersion, Smbios->MinorVersion));
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = InitSmbiosContext (&Context);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // <IndustryStandard/SmBios.h> and <Protocol/Smbios.h> must agree.
+  //
+  ASSERT (sizeof(SMBIOS_STRUCTURE) == sizeof(EFI_SMBIOS_TABLE_HEADER));
+
+  Status = ScanQemuSmbios (Smbios, ImageHandle, Context);
+  if (EFI_ERROR (Status)) {
+    goto Cleanup;
+  }
+
+  Status = InstallDefaultTables (Smbios, ImageHandle, Context);
+
+Cleanup:
+  UninitSmbiosContext (Context);
+  return Status;
+}
diff --git a/OvmfPkg/SmbiosPlatformDxe/QemuLegacy.h b/OvmfPkg/SmbiosPlatformDxe/QemuLegacy.h
new file mode 100644
index 0000000..40d5ad3
--- /dev/null
+++ b/OvmfPkg/SmbiosPlatformDxe/QemuLegacy.h
@@ -0,0 +1,52 @@
+/** @file
+  This header file provides QEMU-specific public prototypes for the main driver
+  file, "SmbiosPlatformDxe.c".
+
+  Copyright (C) 2013, Red Hat, Inc.
+
+  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 _QEMU_LEGACY_H_
+#define _QEMU_LEGACY_H_
+
+#include <Protocol/Smbios.h>
+
+
+/**
+  Fetch and install SMBIOS tables on the QEMU hypervisor.
+
+  First, tables provided by QEMU in entirety are installed verbatim.
+
+  Then the function prepares some of the remaining tables required by the
+  SMBIOS-2.7.1 specification. For each such table,
+  - if QEMU provides any fields for the table, they take effect verbatim,
+  - remaining fields are set by this function.
+
+  @param[in] Smbios       The EFI_SMBIOS_PROTOCOL instance used for installing
+                          the SMBIOS tables.
+  @param[in] ImageHandle  The image handle of the calling module, passed as
+                          ProducerHandle to the Smbios->Add() call.
+
+  @retval EFI_SUCCESS            All tables have been installed.
+  @retval EFI_UNSUPPORTED        The pair (Smbios->MajorVersion,
+                                 Smbios->MinorVersion) precedes (2, 3)
+                                 lexicographically.
+  @return                        Error codes returned by Smbios->Add() or
+                                 internal functions. Some tables may not have
+                                 been installed or fully patched.
+**/
+EFI_STATUS
+EFIAPI
+InstallQemuSmbiosTables (
+  IN EFI_SMBIOS_PROTOCOL *Smbios,
+  IN EFI_HANDLE          ImageHandle
+  );
+
+#endif
diff --git a/OvmfPkg/SmbiosPlatformDxe/QemuLegacyInternal.h b/OvmfPkg/SmbiosPlatformDxe/QemuLegacyInternal.h
new file mode 100644
index 0000000..8613407
--- /dev/null
+++ b/OvmfPkg/SmbiosPlatformDxe/QemuLegacyInternal.h
@@ -0,0 +1,221 @@
+/** @file
+  This header provides common includes, and communicates internal types,
+  function prototypes and macros between "Qemu.c" and "QemuTypeXX.c", that
+  relate to the installation and patching of SMBIOS tables on the QEMU
+  platform.
+
+  Copyright (C) 2013, Red Hat, Inc.
+
+  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 _QEMU_LEGACY_INTERNAL_H_
+#define _QEMU_LEGACY_INTERNAL_H_
+
+#include <IndustryStandard/SmBios.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Protocol/Smbios.h>
+
+
+//
+// Type identifiers of all tables mandated by the SMBIOS-2.7.1 specification
+// fall strictly under this limit.
+//
+#define TABLE_TYPE_LIMIT (EFI_SMBIOS_TYPE_SYSTEM_BOOT_INFORMATION + 1)
+
+
+//
+// Track a patch in dynamic memory, originating from a QEMU SMBIOS firmware
+// configuration entry with ET_FIELD type.
+//
+typedef struct {
+  UINT8  *Base;
+  UINT16 Size;
+} PATCH;
+
+
+#define FIELD_OFFSET_MINIMUM  ((INT32) sizeof(SMBIOS_STRUCTURE))
+#define PATCH_SUBSCRIPT_LIMIT (255 - FIELD_OFFSET_MINIMUM)
+
+//
+// The following structure tracks the installation of each SMBIOS table with a
+// type below TABLE_TYPE_LIMIT, and captures QEMU SMBIOS firmware configuration
+// entries with ET_FIELD type that target the default table for the same type.
+//
+typedef struct {
+  BOOLEAN Installed; // at least one instance of the type has been installed
+
+  PATCH Patch[PATCH_SUBSCRIPT_LIMIT]; // Patches indexed by the field offset
+                                      // that they target in this specific
+                                      // table type. Patching the SMBIOS table
+                                      // header is not allowed, hence we can
+                                      // shift down field offsets. An unused
+                                      // element has zeroed-out fields.
+} TABLE_CONTEXT;
+
+
+//
+// Track the installation of, and stored patches for, all table types below
+// TABLE_TYPE_LIMIT.
+//
+typedef struct {
+  TABLE_CONTEXT Table[TABLE_TYPE_LIMIT];
+} BUILD_CONTEXT;
+
+
+//
+// Convenience / safety macro for defining C structure types for default SMBIOS
+// tables.
+//
+// Rules of use:
+// - Use only within #pragma pack(1).
+// - This macro depends on the macro
+//   "OVMF_TYPE ## TableType ## _STRINGS" specifying the text strings
+//   (unformatted area) for TableType. Each "QemuTypeXX.c" file needs to
+//   provide said macro before using the one below.
+//
+#define OVMF_SMBIOS(TableType)                                                \
+          typedef struct {                                                    \
+            SMBIOS_TABLE_TYPE##TableType Base;                                \
+            UINT8             Strings[sizeof OVMF_TYPE##TableType##_STRINGS]; \
+          } OVMF_TYPE##TableType
+
+
+//
+// Convenience / safety macro for patching a field in the formatted area of
+// an SMBIOS table.
+//
+#define PATCH_FORMATTED(Context, TableType, OvmfTablePtr, FieldName)      \
+          PatchSmbiosFormatted (                                          \
+            Context,                                                      \
+            TableType,                                                    \
+            (UINT16) OFFSET_OF (SMBIOS_TABLE_TYPE##TableType, FieldName), \
+            (UINT16) sizeof (OvmfTablePtr)->Base.FieldName,               \
+            (UINT8 *) (OvmfTablePtr)                                      \
+            )
+
+
+/**
+  Apply a saved patch to a field located in the formatted are of a not yet
+  installed SMBIOS table.
+
+  The patch is looked up based on (Context, TableType, FieldOffset).
+
+  @param[in]  Context      The BUILD_CONTEXT object storing saved patches.
+  @param[in]  TableType    Selects the table type for which the patch has been
+                           saved. It is assumed that the caller has validated
+                           TableType against TABLE_TYPE_LIMIT (upper
+                           exclusive).
+  @param[in]  FieldOffset  Selects the SMBIOS field for which the patch has
+                           been saved. It is assumed that the caller has
+                           validated FieldOffset against FIELD_OFFSET_MINIMUM
+                           (lower inclusive) and 255 (upper exclusive).
+  @param[in]  FieldSize    The caller supplies the size of the field to patch
+                           in FieldSize. The patch saved for
+                           TableType:FieldOffset, if any, is only applied if
+                           its size equals FieldSize.
+  @param[out] TableBase    Base of the SMBIOS table of type TableType in which
+                           the field starting at FieldOffset needs to be
+                           patched.
+
+  @retval EFI_NOT_FOUND          No patch found for TableType:FieldOffset in
+                                 Context. This return value is considered
+                                 informative (ie. non-fatal).
+  @retval EFI_INVALID_PARAMETER  Patch found for TableType:FieldOffset, but its
+                                 size doesn't match FieldSize. This result is
+                                 considered a fatal error of the patch origin.
+  @retval EFI_SUCCESS            The SMBIOS table at TableBase has been patched
+                                 starting at FieldOffset for a length of
+                                 FieldSize.
+**/
+EFI_STATUS
+EFIAPI
+PatchSmbiosFormatted (
+  IN  BUILD_CONTEXT *Context,
+  IN  UINT8         TableType,
+  IN  UINT16        FieldOffset,
+  IN  UINT16        FieldSize,
+  OUT UINT8         *TableBase
+  );
+
+
+//
+// Convenience / safety macro for patching a string in the unformatted area of
+// an SMBIOS table.
+//
+#define PATCH_UNFORMATTED(Smbios, SmbiosHandle, Context, TableType,       \
+          OvmfTablePtr, FieldName)                                        \
+                                                                          \
+          PatchSmbiosUnformatted (                                        \
+            Smbios,                                                       \
+            SmbiosHandle,                                                 \
+            Context,                                                      \
+            TableType,                                                    \
+            (UINT16) OFFSET_OF (SMBIOS_TABLE_TYPE##TableType, FieldName), \
+            (UINT8 *) (OvmfTablePtr)                                      \
+            )
+
+
+/**
+  Apply a saved patch to a text string located in the unformatted area of an
+  already installed SMBIOS table.
+
+  The patch is looked up based on (Context, TableType, FieldOffset).
+
+  @param[in]  Smbios        The EFI_SMBIOS_PROTOCOL instance used previously
+                            for installing the SMBIOS table.
+  @param[in]  SmbiosHandle  The EFI_SMBIOS_HANDLE previously returned by
+                            Smbios->Add().
+  @param[in]  Context       The BUILD_CONTEXT object storing saved patches.
+  @param[in]  TableType     Selects the table type for which the patch has been
+                            saved. It is assumed that the caller has validated
+                            TableType against TABLE_TYPE_LIMIT (upper
+                            exclusive).
+  @param[in]  FieldOffset   Selects the SMBIOS field for which the patch has
+                            been saved. It is assumed that the caller has
+                            validated FieldOffset against FIELD_OFFSET_MINIMUM
+                            (lower inclusive) and 255 (upper exclusive).
+                            It is also assumed that TableBase[FieldOffset]
+                            accesses a field of type SMBIOS_TABLE_STRING, ie. a
+                            field in the formatted area that identifies an
+                            existent text string in the unformatted area. Text
+                            string identifiers are one-based.
+  @param[out] TableBase     Base of the SMBIOS table of type TableType in which
+                            the SMBIOS_TABLE_STRING field at FieldOffset
+                            identifies the existent text string to update.
+
+  @retval EFI_NOT_FOUND          No patch found for TableType:FieldOffset in
+                                 Context. This return value is considered
+                                 informative (ie. non-fatal).
+  @retval EFI_INVALID_PARAMETER  Patch found for TableType:FieldOffset, but it
+                                 doesn't end with a NUL character. This result
+                                 is considered a fatal error of the patch
+                                 origin.
+  @retval EFI_SUCCESS            The text string identified by
+                                 TableBase[FieldOffset] has been replaced in
+                                 the installed SMBIOS table under SmbiosHandle.
+  @return                        Error codes returned by
+                                 Smbios->UpdateString(). EFI_NOT_FOUND shall
+                                 not be returned.
+**/
+EFI_STATUS
+EFIAPI
+PatchSmbiosUnformatted (
+  IN  EFI_SMBIOS_PROTOCOL *Smbios,
+  IN  EFI_SMBIOS_HANDLE   SmbiosHandle,
+  IN  BUILD_CONTEXT       *Context,
+  IN  UINT8               TableType,
+  IN  UINT16              FieldOffset,
+  IN  UINT8               *TableBase
+  );
+
+#endif
diff --git a/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.c b/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.c
index 29948a4..b7c1d0d 100644
--- a/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.c
+++ b/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.c
@@ -1,6 +1,7 @@
 /** @file
   This driver installs SMBIOS information for OVMF
 
+  Copyright (C) 2013, Red Hat, Inc.
   Copyright (c) 2011, Bei Guan <gbtju85@gmail.com>
   Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
 
@@ -15,6 +16,9 @@
 **/
 
 #include "SmbiosPlatformDxe.h"
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+#include "QemuLegacy.h"
+#endif
 
 #define TYPE0_STRINGS \
   "EFI Development Kit II / OVMF\0"     /* Vendor */ \
@@ -27,10 +31,10 @@
 typedef struct {
   SMBIOS_TABLE_TYPE0 Base;
   UINT8              Strings[sizeof(TYPE0_STRINGS)];
-} OVMF_TYPE0;
+} OVMF_DEFAULT_TYPE0;
 #pragma pack()
 
-STATIC CONST OVMF_TYPE0 mOvmfDefaultType0 = {
+STATIC CONST OVMF_DEFAULT_TYPE0 mOvmfDefaultType0 = {
   {
     // SMBIOS_STRUCTURE Hdr
     {
@@ -202,7 +206,14 @@ SmbiosTablePublishEntry (
     SmbiosTables = GetQemuSmbiosTables ();
   }
 
-  if (SmbiosTables != NULL) {
+  if (SmbiosTables == NULL) {
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+    //
+    // Handle QEMU's legacy SMBIOS interface.
+    //
+    Status = InstallQemuSmbiosTables (Smbios, ImageHandle);
+#endif
+  } else {
     Status = InstallAllStructures (Smbios, SmbiosTables);
 
     //
diff --git a/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf b/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
index 3b90aac..8c9f43c 100644
--- a/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
+++ b/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
@@ -1,6 +1,7 @@
 ## @file
 #  This driver installs SMBIOS information for OVMF
 #
+#  Copyright (C) 2013, Red Hat, Inc.
 #  Copyright (c) 2011, Bei Guan <gbtju85@gmail.com>
 #  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
 #
@@ -35,6 +36,7 @@
 
 [Sources.IA32, Sources.X64]
   X86Xen.c
+  QemuLegacy.c
 
 [Sources.ARM, Sources.AARCH64]
   ArmXen.c
-- 
1.8.3.1