summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/CpuHotplugSmm/Smbase.c
diff options
context:
space:
mode:
authorLaszlo Ersek <lersek@redhat.com>2020-02-26 23:11:52 +0100
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2020-03-04 12:22:07 +0000
commit51a6fb41181529e4b50ea13377425bda6bb69ba6 (patch)
tree9e03f90aed2a925372cbe49c26738a1c569435a0 /OvmfPkg/CpuHotplugSmm/Smbase.c
parent63c89da242f3a49e0dbdfac0d2a29ae0daa73970 (diff)
downloadedk2-51a6fb41181529e4b50ea13377425bda6bb69ba6.tar.gz
edk2-51a6fb41181529e4b50ea13377425bda6bb69ba6.tar.bz2
edk2-51a6fb41181529e4b50ea13377425bda6bb69ba6.zip
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>
Diffstat (limited to 'OvmfPkg/CpuHotplugSmm/Smbase.c')
-rw-r--r--OvmfPkg/CpuHotplugSmm/Smbase.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/OvmfPkg/CpuHotplugSmm/Smbase.c b/OvmfPkg/CpuHotplugSmm/Smbase.c
index ea21153d91..170571221d 100644
--- a/OvmfPkg/CpuHotplugSmm/Smbase.c
+++ b/OvmfPkg/CpuHotplugSmm/Smbase.c
@@ -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;
+}