OvmfPkg/CpuHotplugSmm: introduce First SMI Handler for hot-added CPUs
Implement the First SMI Handler for hot-added CPUs, in NASM. Add the interfacing C-language function that the SMM Monarch calls. This function launches and coordinates SMBASE relocation for a hot-added CPU. 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> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200226221156.29589-13-lersek@redhat.com> Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Tested-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
This commit is contained in:
committed by
mergify[bot]
parent
63c89da242
commit
51a6fb4118
@@ -7,13 +7,21 @@
|
||||
**/
|
||||
|
||||
#include <Base.h> // BASE_1MB
|
||||
#include <Library/BaseLib.h> // CpuPause()
|
||||
#include <Library/BaseMemoryLib.h> // CopyMem()
|
||||
#include <Library/DebugLib.h> // DEBUG()
|
||||
#include <Library/LocalApicLib.h> // SendInitSipiSipi()
|
||||
#include <Library/SynchronizationLib.h> // InterlockedCompareExchange64()
|
||||
#include <Register/Intel/SmramSaveStateMap.h> // SMM_DEFAULT_SMBASE
|
||||
|
||||
#include "FirstSmiHandlerContext.h" // FIRST_SMI_HANDLER_CONTEXT
|
||||
|
||||
#include "Smbase.h"
|
||||
|
||||
extern CONST UINT8 mPostSmmPen[];
|
||||
extern CONST UINT16 mPostSmmPenSize;
|
||||
extern CONST UINT8 mFirstSmiHandler[];
|
||||
extern CONST UINT16 mFirstSmiHandlerSize;
|
||||
|
||||
/**
|
||||
Allocate a non-SMRAM reserved memory page for the Post-SMM Pen for hot-added
|
||||
@@ -108,3 +116,152 @@ SmbaseReleasePostSmmPen (
|
||||
{
|
||||
BootServices->FreePages (PenAddress, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
Place the handler routine for the first SMIs of hot-added CPUs at
|
||||
(SMM_DEFAULT_SMBASE + SMM_HANDLER_OFFSET).
|
||||
|
||||
Note that this effects an "SMRAM to SMRAM" copy.
|
||||
|
||||
Additionally, shut the APIC ID gate in FIRST_SMI_HANDLER_CONTEXT.
|
||||
|
||||
This function may only be called from the entry point function of the driver,
|
||||
and only after PcdQ35SmramAtDefaultSmbase has been determined to be TRUE.
|
||||
**/
|
||||
VOID
|
||||
SmbaseInstallFirstSmiHandler (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
FIRST_SMI_HANDLER_CONTEXT *Context;
|
||||
|
||||
CopyMem ((VOID *)(UINTN)(SMM_DEFAULT_SMBASE + SMM_HANDLER_OFFSET),
|
||||
mFirstSmiHandler, mFirstSmiHandlerSize);
|
||||
|
||||
Context = (VOID *)(UINTN)SMM_DEFAULT_SMBASE;
|
||||
Context->ApicIdGate = MAX_UINT64;
|
||||
}
|
||||
|
||||
/**
|
||||
Relocate the SMBASE on a hot-added CPU. Then pen the hot-added CPU in the
|
||||
normal RAM reserved memory page, set up earlier with
|
||||
SmbaseAllocatePostSmmPen() and SmbaseReinstallPostSmmPen().
|
||||
|
||||
The SMM Monarch is supposed to call this function from the root MMI handler.
|
||||
|
||||
The SMM Monarch is responsible for calling SmbaseInstallFirstSmiHandler(),
|
||||
SmbaseAllocatePostSmmPen(), and SmbaseReinstallPostSmmPen() before calling
|
||||
this function.
|
||||
|
||||
If the OS maliciously boots the hot-added CPU ahead of letting the ACPI CPU
|
||||
hotplug event handler broadcast the CPU hotplug MMI, then the hot-added CPU
|
||||
returns to the OS rather than to the pen, upon RSM. In that case, this
|
||||
function will hang forever (unless the OS happens to signal back through the
|
||||
last byte of the pen page).
|
||||
|
||||
@param[in] ApicId The APIC ID of the hot-added CPU whose SMBASE should
|
||||
be relocated.
|
||||
|
||||
@param[in] Smbase The new SMBASE address. The root MMI handler is
|
||||
responsible for passing in a free ("unoccupied")
|
||||
SMBASE address that was pre-configured by
|
||||
PiSmmCpuDxeSmm in CPU_HOT_PLUG_DATA.
|
||||
|
||||
@param[in] PenAddress The address of the Post-SMM Pen for hot-added CPUs, as
|
||||
returned by SmbaseAllocatePostSmmPen(), and installed
|
||||
by SmbaseReinstallPostSmmPen().
|
||||
|
||||
@retval EFI_SUCCESS The SMBASE of the hot-added CPU with APIC ID
|
||||
ApicId has been relocated to Smbase. The
|
||||
hot-added CPU has reported back about leaving
|
||||
SMM.
|
||||
|
||||
@retval EFI_PROTOCOL_ERROR Synchronization bug encountered around
|
||||
FIRST_SMI_HANDLER_CONTEXT.ApicIdGate.
|
||||
|
||||
@retval EFI_INVALID_PARAMETER Smbase does not fit in 32 bits. No relocation
|
||||
has been attempted.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmbaseRelocate (
|
||||
IN APIC_ID ApicId,
|
||||
IN UINTN Smbase,
|
||||
IN UINT32 PenAddress
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
volatile UINT8 *SmmVacated;
|
||||
volatile FIRST_SMI_HANDLER_CONTEXT *Context;
|
||||
UINT64 ExchangeResult;
|
||||
|
||||
if (Smbase > MAX_UINT32) {
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
DEBUG ((DEBUG_ERROR, "%a: ApicId=" FMT_APIC_ID " Smbase=0x%Lx: %r\n",
|
||||
__FUNCTION__, ApicId, (UINT64)Smbase, Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
SmmVacated = (UINT8 *)(UINTN)PenAddress + (EFI_PAGE_SIZE - 1);
|
||||
Context = (VOID *)(UINTN)SMM_DEFAULT_SMBASE;
|
||||
|
||||
//
|
||||
// Clear AboutToLeaveSmm, so we notice when the hot-added CPU is just about
|
||||
// to reach RSM, and we can proceed to polling the last byte of the reserved
|
||||
// page (which could be attacked by the OS).
|
||||
//
|
||||
Context->AboutToLeaveSmm = 0;
|
||||
|
||||
//
|
||||
// Clear the last byte of the reserved page, so we notice when the hot-added
|
||||
// CPU checks back in from the pen.
|
||||
//
|
||||
*SmmVacated = 0;
|
||||
|
||||
//
|
||||
// Boot the hot-added CPU.
|
||||
//
|
||||
// If the OS is benign, and so the hot-added CPU is still in RESET state,
|
||||
// then the broadcast SMI is still pending for it; it will now launch
|
||||
// directly into SMM.
|
||||
//
|
||||
// If the OS is malicious, the hot-added CPU has been booted already, and so
|
||||
// it is already spinning on the APIC ID gate. In that case, the
|
||||
// INIT-SIPI-SIPI below will be ignored.
|
||||
//
|
||||
SendInitSipiSipi (ApicId, PenAddress);
|
||||
|
||||
//
|
||||
// Expose the desired new SMBASE value to the hot-added CPU.
|
||||
//
|
||||
Context->NewSmbase = (UINT32)Smbase;
|
||||
|
||||
//
|
||||
// Un-gate SMBASE relocation for the hot-added CPU whose APIC ID is ApicId.
|
||||
//
|
||||
ExchangeResult = InterlockedCompareExchange64 (&Context->ApicIdGate,
|
||||
MAX_UINT64, ApicId);
|
||||
if (ExchangeResult != MAX_UINT64) {
|
||||
Status = EFI_PROTOCOL_ERROR;
|
||||
DEBUG ((DEBUG_ERROR, "%a: ApicId=" FMT_APIC_ID " ApicIdGate=0x%Lx: %r\n",
|
||||
__FUNCTION__, ApicId, ExchangeResult, Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Wait until the hot-added CPU is just about to execute RSM.
|
||||
//
|
||||
while (Context->AboutToLeaveSmm == 0) {
|
||||
CpuPause ();
|
||||
}
|
||||
|
||||
//
|
||||
// Now wait until the hot-added CPU reports back from the pen (or the OS
|
||||
// attacks the last byte of the reserved page).
|
||||
//
|
||||
while (*SmmVacated == 0) {
|
||||
CpuPause ();
|
||||
}
|
||||
|
||||
Status = EFI_SUCCESS;
|
||||
return Status;
|
||||
}
|
||||
|
Reference in New Issue
Block a user