With "PcdCpuSmmEnableBspElection" set to FALSE, PiSmmCpuDxeSmm always considers the processor with index 0 to be the SMM Monarch (a.k.a. the SMM BSP). The SMM Monarch handles the SMI for real, while the other CPUs wait in their SMM loops. In a subsequent patch, we want to set "PcdCpuHotPlugSupport" to TRUE. For that, PiCpuSmmEntry() [UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c] forces us with an ASSERT() to set "PcdCpuSmmEnableBspElection" to TRUE as well. To satisfy that expectation, we can simply remove our current "PcdCpuSmmEnableBspElection|FALSE" setting, and inherit the default TRUE value from "UefiCpuPkg.dec". This causes "mSmmMpSyncData->BspIndex" in PiSmmCpuDxeSmm to lose its static zero value (standing for CPU#0); instead it becomes (-1) in general, and the SMM Monarch is elected anew on every SMI. The default SMM Monarch Election is basically a race -- whichever CPU can flip "mSmmMpSyncData->BspIndex" from (-1) to its own index, becomes king, for handling that SMI. Refer to SmiRendezvous() [UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c]. I consider this non-determinism less than ideal on QEMU/KVM; it would be nice to stick with a "mostly permanent" SMM Monarch even with the Election enabled. We can do that by implementing the PlatformSmmBspElection() API in the SmmCpuPlatformHookLibQemu instance: The IA32 APIC Base MSR can be read on each CPU concurrently, and it will report the BSP bit as set only on the current Boot Service Processor. QEMU marks CPU#0 as the BSP, by default. Elect the current BSP, as reported by QEMU, for the SMM Monarch role. (Note that the QEMU commit history is not entirely consistent on whether QEMU/KVM may mark a CPU with nonzero index as the BSP: - At tag v4.2.0, "target/i386/cpu.c" has a comment saying "We hard-wire the BSP to the first CPU". This comment goes back to commit 6cb2996cef5e ("x86: Extend validity of bsp_to_cpu", 2010-03-04). - Compare commit 9cb11fd7539b ("target-i386: clear bsp bit when designating bsp", 2015-04-02) though, especially considering KVM. Either way, this OvmfPkg patch is *not* dependent on CPU index 0; it just takes the race on every SMI out of the game.) One benefit of using a "mostly permanent" SMM Monarch / BSP is that we can continue testing the SMM CPU synchronization by deterministically entering the firmware on the BSP, vs. on an AP, from Linux guests: $ time taskset -c 0 efibootmgr $ time taskset -c 1 efibootmgr (See <https://github.com/tianocore/tianocore.github.io/wiki/Testing-SMM-with-QEMU,-KVM-and-libvirt#uefi-variable-access-test>.) Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Igor Mammedov <imammedo@redhat.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Suggested-by: Igor Mammedov <imammedo@redhat.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512#c5 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200226221156.29589-5-lersek@redhat.com> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Tested-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
116 lines
3.5 KiB
C
116 lines
3.5 KiB
C
/** @file
|
|
SMM CPU Platform Hook library instance for QEMU.
|
|
|
|
Copyright (c) 2020, Red Hat, Inc.
|
|
Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
#include <Library/BaseLib.h> // AsmReadMsr64()
|
|
#include <PiSmm.h>
|
|
#include <Register/Intel/ArchitecturalMsr.h> // MSR_IA32_APIC_BASE_REGISTER
|
|
|
|
#include <Library/SmmCpuPlatformHookLib.h>
|
|
|
|
/**
|
|
Checks if platform produces a valid SMI.
|
|
|
|
This function checks if platform produces a valid SMI. This function is
|
|
called at SMM entry to detect if this is a spurious SMI. This function
|
|
must be implemented in an MP safe way because it is called by multiple CPU
|
|
threads.
|
|
|
|
@retval TRUE There is a valid SMI
|
|
@retval FALSE There is no valid SMI
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
PlatformValidSmi (
|
|
VOID
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Clears platform top level SMI status bit.
|
|
|
|
This function clears platform top level SMI status bit.
|
|
|
|
@retval TRUE The platform top level SMI status is cleared.
|
|
@retval FALSE The platform top level SMI status cannot be
|
|
cleared.
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
ClearTopLevelSmiStatus (
|
|
VOID
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Performs platform specific way of SMM BSP election.
|
|
|
|
This function performs platform specific way of SMM BSP election.
|
|
|
|
@param IsBsp Output parameter. TRUE: the CPU this function
|
|
executes on is elected to be the SMM BSP. FALSE:
|
|
the CPU this function executes on is to be SMM AP.
|
|
|
|
@retval EFI_SUCCESS The function executes successfully.
|
|
@retval EFI_NOT_READY The function does not determine whether this CPU
|
|
should be BSP or AP. This may occur if hardware
|
|
init sequence to enable the determination is yet to
|
|
be done, or the function chooses not to do BSP
|
|
election and will let SMM CPU driver to use its
|
|
default BSP election process.
|
|
@retval EFI_DEVICE_ERROR The function cannot determine whether this CPU
|
|
should be BSP or AP due to hardware error.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PlatformSmmBspElection (
|
|
OUT BOOLEAN *IsBsp
|
|
)
|
|
{
|
|
MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;
|
|
|
|
ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
|
|
*IsBsp = (BOOLEAN)(ApicBaseMsr.Bits.BSP == 1);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get platform page table attribute.
|
|
|
|
This function gets page table attribute of platform.
|
|
|
|
@param Address Input parameter. Obtain the page table entries
|
|
attribute on this address.
|
|
@param PageSize Output parameter. The size of the page.
|
|
@param NumOfPages Output parameter. Number of page.
|
|
@param PageAttribute Output parameter. Paging Attributes (WB, UC, etc).
|
|
|
|
@retval EFI_SUCCESS The platform page table attribute from the address
|
|
is determined.
|
|
@retval EFI_UNSUPPORTED The platform does not support getting page table
|
|
attribute for the address.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetPlatformPageTableAttribute (
|
|
IN UINT64 Address,
|
|
IN OUT SMM_PAGE_SIZE_TYPE *PageSize,
|
|
IN OUT UINTN *NumOfPages,
|
|
IN OUT UINTN *PageAttribute
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|