09a274d82f
The "bios-tables-test" program in QEMU's test suite locates the RSD PTR ACPI table in guest RAM, and (chasing pointers to other ACPI tables) performs various sanity checks on the QEMU-generated and firmware-installed tables. Currently this set of test cases doesn't work with UEFI guests. The ACPI spec defines distinct methods for OSPM to locate the RSD PTR on traditional BIOS vs. UEFI platforms, and the UEFI method is more difficult to implement from the hypervisor side with just raw guest memory access. Add a UEFI application (to be booted in the UEFI guest) that populates a small, MB-aligned structure in guest RAM. The structure begins with a signature GUID. The hypervisor should loop over all MB-aligned pages in guest RAM until one matches the signature GUID at offset 0, at which point the hypervisor can fetch the RSDP address field(s) from the structure. QEMU's test logic currently spins on a pre-determined guest address, until that address assumes a magic value. The method described in this patch is conceptually the same ("busy loop until match is found"), except there is no hard-coded address. This plays a lot more nicely with UEFI guest firmware (we'll be able to use the normal page allocation UEFI service). Given the size of EFI_GUID (16 bytes -- 128 bits), mismatches should be astronomically unlikely. In addition, given the typical guest RAM size for such tests (128 MB), there are 128 locations to check in one iteration of the "outer" loop, which shouldn't introduce an intolerable delay after the guest stores the RSDP address(es), and then the GUID. The GUID that the hypervisor should search for is AB87A6B1-2034-BDA0-71BD-375007757785 Expressed as a byte array: { 0xb1, 0xa6, 0x87, 0xab, 0x34, 0x20, 0xa0, 0xbd, 0x71, 0xbd, 0x37, 0x50, 0x07, 0x75, 0x77, 0x85 } Note that in the patch, we define "gBiosTablesTestGuid" with all bits inverted. This is a simple method to prevent the UEFI binary, which incorporates "gBiosTablesTestGuid", from matching the actual GUID in guest RAM. The UEFI application is written against the edk2 framework, which was introduced earlier as a git submodule. The next patch will provide build scripts for maintainers. The source code follows the edk2 coding style, and is licensed under the 2-clause BSDL (in case someone would like to include UefiTestToolsPkg content in a different edk2 platform). The "UefiTestToolsPkg.dsc" platform description file resolves the used edk2 library classes to instances (= library implementations) such that the UEFI binaries inherit no platform dependencies. They are expected to run on any system that conforms to the UEFI-2.3.1 spec (which was released in 2012). The arch-specific build options are carried over from edk2's ArmVirtPkg and OvmfPkg platforms. Cc: "Michael S. Tsirkin" <mst@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Igor Mammedov <imammedo@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Cc: Shannon Zhao <shannon.zhaosl@gmail.com> Signed-off-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20190204160325.4914-4-lersek@redhat.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
131 lines
4.2 KiB
C
131 lines
4.2 KiB
C
/** @file
|
|
Populate the BIOS_TABLES_TEST structure.
|
|
|
|
Copyright (C) 2019, Red Hat, Inc.
|
|
|
|
This program and the accompanying materials are licensed and made available
|
|
under the terms and conditions of the BSD License that 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 <Guid/Acpi.h>
|
|
#include <Guid/BiosTablesTest.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiLib.h>
|
|
|
|
/**
|
|
Wait for a keypress with a message that the application is about to exit.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
WaitForExitKeyPress (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Idx;
|
|
EFI_INPUT_KEY Key;
|
|
|
|
if (gST->ConIn == NULL) {
|
|
return;
|
|
}
|
|
AsciiPrint ("%a: press any key to exit\n", gEfiCallerBaseName);
|
|
Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Idx);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BiosTablesTestMain (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
VOID *Pages;
|
|
volatile BIOS_TABLES_TEST *BiosTablesTest;
|
|
CONST VOID *Rsdp10;
|
|
CONST VOID *Rsdp20;
|
|
CONST EFI_CONFIGURATION_TABLE *ConfigTable;
|
|
CONST EFI_CONFIGURATION_TABLE *ConfigTablesEnd;
|
|
volatile EFI_GUID *InverseSignature;
|
|
UINTN Idx;
|
|
|
|
Pages = AllocateAlignedPages (EFI_SIZE_TO_PAGES (sizeof *BiosTablesTest),
|
|
SIZE_1MB);
|
|
if (Pages == NULL) {
|
|
AsciiErrorPrint ("%a: AllocateAlignedPages() failed\n",
|
|
gEfiCallerBaseName);
|
|
//
|
|
// Assuming the application was launched by the boot manager as a boot
|
|
// loader, exiting with error will cause the boot manager to proceed with
|
|
// the remaining boot options. If there are no other boot options, the boot
|
|
// manager menu will be pulled up. Give the user a chance to read the error
|
|
// message.
|
|
//
|
|
WaitForExitKeyPress ();
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Locate both gEfiAcpi10TableGuid and gEfiAcpi20TableGuid config tables in
|
|
// one go.
|
|
//
|
|
Rsdp10 = NULL;
|
|
Rsdp20 = NULL;
|
|
ConfigTable = gST->ConfigurationTable;
|
|
ConfigTablesEnd = gST->ConfigurationTable + gST->NumberOfTableEntries;
|
|
while ((Rsdp10 == NULL || Rsdp20 == NULL) && ConfigTable < ConfigTablesEnd) {
|
|
if (CompareGuid (&ConfigTable->VendorGuid, &gEfiAcpi10TableGuid)) {
|
|
Rsdp10 = ConfigTable->VendorTable;
|
|
} else if (CompareGuid (&ConfigTable->VendorGuid, &gEfiAcpi20TableGuid)) {
|
|
Rsdp20 = ConfigTable->VendorTable;
|
|
}
|
|
++ConfigTable;
|
|
}
|
|
|
|
AsciiPrint ("%a: BiosTablesTest=%p Rsdp10=%p Rsdp20=%p\n",
|
|
gEfiCallerBaseName, Pages, Rsdp10, Rsdp20);
|
|
|
|
//
|
|
// Store the RSD PTR address(es) first, then the signature second.
|
|
//
|
|
BiosTablesTest = Pages;
|
|
BiosTablesTest->Rsdp10 = (UINTN)Rsdp10;
|
|
BiosTablesTest->Rsdp20 = (UINTN)Rsdp20;
|
|
|
|
MemoryFence();
|
|
|
|
InverseSignature = &BiosTablesTest->InverseSignatureGuid;
|
|
InverseSignature->Data1 = gBiosTablesTestGuid.Data1;
|
|
InverseSignature->Data1 ^= MAX_UINT32;
|
|
InverseSignature->Data2 = gBiosTablesTestGuid.Data2;
|
|
InverseSignature->Data2 ^= MAX_UINT16;
|
|
InverseSignature->Data3 = gBiosTablesTestGuid.Data3;
|
|
InverseSignature->Data3 ^= MAX_UINT16;
|
|
for (Idx = 0; Idx < sizeof InverseSignature->Data4; ++Idx) {
|
|
InverseSignature->Data4[Idx] = gBiosTablesTestGuid.Data4[Idx];
|
|
InverseSignature->Data4[Idx] ^= MAX_UINT8;
|
|
}
|
|
|
|
//
|
|
// The wait below has dual purpose. First, it blocks the application without
|
|
// wasting VCPU cycles while the hypervisor is scanning guest RAM. Second,
|
|
// assuming the application was launched by the boot manager as a boot
|
|
// loader, exiting the app with success causes the boot manager to pull up
|
|
// the boot manager menu at once (regardless of other boot options); the wait
|
|
// gives the user a chance to read the info printed above.
|
|
//
|
|
WaitForExitKeyPress ();
|
|
return EFI_SUCCESS;
|
|
}
|