2015-10-19 21:13:13 +02:00
|
|
|
/** @file
|
|
|
|
Page table manipulation functions for IA-32 processors
|
|
|
|
|
UefiCpuPkg/PiSmmCpuDxeSmm: Check PDE entry exist or not before use
Before the commit 701b5797 & 4ceefd6d, 2MB-page will be created to
cover [0: 4G] by default if SmmProfile enabled, and it will be go
through to change 2MB-page into 4KB-page during page table update
(InitPaging). If so, there was no problem to assert PDE entry exist
in the RestorePageTableBelow4G.
But after above commits, PageTableMap API is used to create/update
the page table, 1G-page will be the default page table mode, and
only covers the limited address range. Those not covered ranges
will be marked as non-present in 1g-page level address. If so,
2M-page address might not exist, it's incorrect to assert PDE
entry exist in the RestorePageTableBelow4G.
The correct behavior should check PDE entry exist or not, if not,
PDE should be allocated and assigned to PDPTE.
Note:
RestorePageTableBelow4G () does not use 1G page size entries
for the creation of new pages, maintaining consistency with the
behavior of the original code.
The purpose of this patch is to ensure that a Page Directory Entry
(PDE) exists prior to its usage.
Signed-off-by: Jiaxin Wu <jiaxin.wu@intel.com>
2024-07-15 04:16:12 +02:00
|
|
|
Copyright (c) 2009 - 2024, Intel Corporation. All rights reserved.<BR>
|
2017-02-26 18:43:07 +01:00
|
|
|
Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
|
|
|
|
|
2019-04-04 01:07:22 +02:00
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
2015-10-19 21:13:13 +02:00
|
|
|
|
|
|
|
**/
|
|
|
|
|
2024-06-24 11:56:00 +02:00
|
|
|
#include "PiSmmCpuCommon.h"
|
2015-10-19 21:13:13 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
Create PageTable for SMM use.
|
|
|
|
|
|
|
|
@return PageTable Address
|
|
|
|
|
|
|
|
**/
|
|
|
|
UINT32
|
|
|
|
SmmInitPageTable (
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINTN PageFaultHandlerHookAddress;
|
|
|
|
IA32_IDT_GATE_DESCRIPTOR *IdtEntry;
|
2016-11-16 15:25:56 +01:00
|
|
|
EFI_STATUS Status;
|
2015-10-19 21:13:13 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Initialize spin lock
|
|
|
|
//
|
2016-03-22 03:15:53 +01:00
|
|
|
InitializeSpinLock (mPFLock);
|
2015-10-19 21:13:13 +02:00
|
|
|
|
2017-08-24 04:59:14 +02:00
|
|
|
mPhysicalAddressBits = 32;
|
2023-06-07 09:46:58 +02:00
|
|
|
mPagingMode = PagingPae;
|
2017-08-24 04:59:14 +02:00
|
|
|
|
2024-07-08 06:48:32 +02:00
|
|
|
if (mSmmProfileEnabled ||
|
2018-08-20 05:35:58 +02:00
|
|
|
HEAP_GUARD_NONSTOP_MODE ||
|
|
|
|
NULL_DETECTION_NONSTOP_MODE)
|
|
|
|
{
|
2015-10-19 21:13:13 +02:00
|
|
|
//
|
|
|
|
// Set own Page Fault entry instead of the default one, because SMM Profile
|
|
|
|
// feature depends on IRET instruction to do Single Step
|
|
|
|
//
|
|
|
|
PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
|
|
|
|
IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base;
|
|
|
|
IdtEntry += EXCEPT_IA32_PAGE_FAULT;
|
|
|
|
IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
|
|
|
|
IdtEntry->Bits.Reserved_0 = 0;
|
|
|
|
IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
|
|
|
|
IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);
|
|
|
|
} else {
|
|
|
|
//
|
|
|
|
// Register SMM Page Fault Handler
|
|
|
|
//
|
2016-11-16 15:25:56 +01:00
|
|
|
Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
2015-10-19 21:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Additional SMM IDT initialization for SMM stack guard
|
|
|
|
//
|
|
|
|
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
|
|
|
|
InitializeIDTSmmStackGuard ();
|
|
|
|
}
|
2021-12-05 23:54:17 +01:00
|
|
|
|
2023-05-15 09:47:54 +02:00
|
|
|
return GenSmmPageTable (PagingPae, mPhysicalAddressBits);
|
2015-10-19 21:13:13 +02:00
|
|
|
}
|
|
|
|
|
UefiCpuPkg/PiSmmCpuDxeSmm: Check PDE entry exist or not before use
Before the commit 701b5797 & 4ceefd6d, 2MB-page will be created to
cover [0: 4G] by default if SmmProfile enabled, and it will be go
through to change 2MB-page into 4KB-page during page table update
(InitPaging). If so, there was no problem to assert PDE entry exist
in the RestorePageTableBelow4G.
But after above commits, PageTableMap API is used to create/update
the page table, 1G-page will be the default page table mode, and
only covers the limited address range. Those not covered ranges
will be marked as non-present in 1g-page level address. If so,
2M-page address might not exist, it's incorrect to assert PDE
entry exist in the RestorePageTableBelow4G.
The correct behavior should check PDE entry exist or not, if not,
PDE should be allocated and assigned to PDPTE.
Note:
RestorePageTableBelow4G () does not use 1G page size entries
for the creation of new pages, maintaining consistency with the
behavior of the original code.
The purpose of this patch is to ensure that a Page Directory Entry
(PDE) exists prior to its usage.
Signed-off-by: Jiaxin Wu <jiaxin.wu@intel.com>
2024-07-15 04:16:12 +02:00
|
|
|
/**
|
|
|
|
Allocate free Page for PageFault handler use.
|
|
|
|
|
|
|
|
@return Page address.
|
|
|
|
|
|
|
|
**/
|
|
|
|
UINT64
|
|
|
|
AllocPage (
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
CpuDeadLoop ();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-19 21:13:13 +02:00
|
|
|
/**
|
|
|
|
ThePage Fault handler wrapper for SMM use.
|
|
|
|
|
|
|
|
@param InterruptType Defines the type of interrupt or exception that
|
|
|
|
occurred on the processor.This parameter is processor architecture specific.
|
|
|
|
@param SystemContext A pointer to the processor context when
|
|
|
|
the interrupt occurred on the processor.
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
EFIAPI
|
|
|
|
SmiPFHandler (
|
2017-04-01 13:39:22 +02:00
|
|
|
IN EFI_EXCEPTION_TYPE InterruptType,
|
|
|
|
IN EFI_SYSTEM_CONTEXT SystemContext
|
2015-10-19 21:13:13 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
UINTN PFAddress;
|
2016-11-22 08:05:11 +01:00
|
|
|
UINTN GuardPageAddress;
|
|
|
|
UINTN CpuIndex;
|
2015-10-19 21:13:13 +02:00
|
|
|
|
|
|
|
ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
|
|
|
|
|
2016-03-22 03:15:53 +01:00
|
|
|
AcquireSpinLock (mPFLock);
|
2015-10-19 21:13:13 +02:00
|
|
|
|
|
|
|
PFAddress = AsmReadCr2 ();
|
|
|
|
|
2016-11-22 08:05:11 +01:00
|
|
|
//
|
|
|
|
// If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
|
|
|
|
// or SMM page protection violation.
|
|
|
|
//
|
|
|
|
if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&
|
2015-10-19 21:13:13 +02:00
|
|
|
(PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)))
|
|
|
|
{
|
2017-04-01 13:39:22 +02:00
|
|
|
DumpCpuContext (InterruptType, SystemContext);
|
2016-11-22 08:05:11 +01:00
|
|
|
CpuIndex = GetCpuIndex ();
|
|
|
|
GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);
|
|
|
|
if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
|
|
|
|
(PFAddress >= GuardPageAddress) &&
|
|
|
|
(PFAddress < (GuardPageAddress + EFI_PAGE_SIZE)))
|
|
|
|
{
|
|
|
|
DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));
|
|
|
|
} else {
|
|
|
|
if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%x)\n", PFAddress));
|
|
|
|
DEBUG_CODE (
|
|
|
|
DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%x)\n", PFAddress));
|
|
|
|
DEBUG_CODE (
|
|
|
|
DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
|
|
|
|
);
|
|
|
|
}
|
2018-08-20 05:35:58 +02:00
|
|
|
|
|
|
|
if (HEAP_GUARD_NONSTOP_MODE) {
|
|
|
|
GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);
|
|
|
|
goto Exit;
|
|
|
|
}
|
2016-11-22 08:05:11 +01:00
|
|
|
}
|
2021-12-05 23:54:17 +01:00
|
|
|
|
2015-10-19 21:13:13 +02:00
|
|
|
CpuDeadLoop ();
|
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1521
We scan the SMM code with ROPgadget.
http://shell-storm.org/project/ROPgadget/
https://github.com/JonathanSalwan/ROPgadget/tree/master
This tool reports the gadget in SMM driver.
This patch enabled CET ShadowStack for X86 SMM.
If CET is supported, SMM will enable CET ShadowStack.
SMM CET will save the OS CET context at SmmEntry and
restore OS CET context at SmmExit.
Test:
1) test Intel internal platform (x64 only, CET enabled/disabled)
Boot test:
CET supported or not supported CPU
on CET supported platform
CET enabled/disabled
PcdCpuSmmCetEnable enabled/disabled
Single core/Multiple core
PcdCpuSmmStackGuard enabled/disabled
PcdCpuSmmProfileEnable enabled/disabled
PcdCpuSmmStaticPageTable enabled/disabled
CET exception test:
#CF generated with PcdCpuSmmStackGuard enabled/disabled.
Other exception test:
#PF for normal stack overflow
#PF for NX protection
#PF for RO protection
CET env test:
Launch SMM in CET enabled/disabled environment (DXE) - no impact to DXE
The test case can be found at
https://github.com/jyao1/SecurityEx/tree/master/ControlFlowPkg
2) test ovmf (both IA32 and X64 SMM, CET disabled only)
test OvmfIa32/Ovmf3264, with -D SMM_REQUIRE.
qemu-system-x86_64.exe -machine q35,smm=on -smp 4
-serial file:serial.log
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd
QEMU emulator version 3.1.0 (v3.1.0-11736-g7a30e7adb0-dirty)
3) not tested
IA32 CET enabled platform
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yao Jiewen <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
2019-02-22 14:30:36 +01:00
|
|
|
goto Exit;
|
2015-10-19 21:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2017-12-06 12:02:04 +01:00
|
|
|
// If a page fault occurs in non-SMRAM range.
|
2015-10-19 21:13:13 +02:00
|
|
|
//
|
|
|
|
if ((PFAddress < mCpuHotPlugData.SmrrBase) ||
|
|
|
|
(PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))
|
|
|
|
{
|
|
|
|
if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
|
2017-12-06 12:02:04 +01:00
|
|
|
DumpCpuContext (InterruptType, SystemContext);
|
2016-10-23 17:19:52 +02:00
|
|
|
DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));
|
2015-10-19 21:13:13 +02:00
|
|
|
DEBUG_CODE (
|
|
|
|
DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
|
|
|
|
);
|
|
|
|
CpuDeadLoop ();
|
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1521
We scan the SMM code with ROPgadget.
http://shell-storm.org/project/ROPgadget/
https://github.com/JonathanSalwan/ROPgadget/tree/master
This tool reports the gadget in SMM driver.
This patch enabled CET ShadowStack for X86 SMM.
If CET is supported, SMM will enable CET ShadowStack.
SMM CET will save the OS CET context at SmmEntry and
restore OS CET context at SmmExit.
Test:
1) test Intel internal platform (x64 only, CET enabled/disabled)
Boot test:
CET supported or not supported CPU
on CET supported platform
CET enabled/disabled
PcdCpuSmmCetEnable enabled/disabled
Single core/Multiple core
PcdCpuSmmStackGuard enabled/disabled
PcdCpuSmmProfileEnable enabled/disabled
PcdCpuSmmStaticPageTable enabled/disabled
CET exception test:
#CF generated with PcdCpuSmmStackGuard enabled/disabled.
Other exception test:
#PF for normal stack overflow
#PF for NX protection
#PF for RO protection
CET env test:
Launch SMM in CET enabled/disabled environment (DXE) - no impact to DXE
The test case can be found at
https://github.com/jyao1/SecurityEx/tree/master/ControlFlowPkg
2) test ovmf (both IA32 and X64 SMM, CET disabled only)
test OvmfIa32/Ovmf3264, with -D SMM_REQUIRE.
qemu-system-x86_64.exe -machine q35,smm=on -smp 4
-serial file:serial.log
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd
QEMU emulator version 3.1.0 (v3.1.0-11736-g7a30e7adb0-dirty)
3) not tested
IA32 CET enabled platform
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yao Jiewen <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
2019-02-22 14:30:36 +01:00
|
|
|
goto Exit;
|
2015-10-19 21:13:13 +02:00
|
|
|
}
|
2018-08-20 05:35:58 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// If NULL pointer was just accessed
|
|
|
|
//
|
|
|
|
if (((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0) &&
|
|
|
|
(PFAddress < EFI_PAGE_SIZE))
|
|
|
|
{
|
|
|
|
DumpCpuContext (InterruptType, SystemContext);
|
|
|
|
DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));
|
|
|
|
DEBUG_CODE (
|
|
|
|
DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
|
|
|
|
);
|
|
|
|
|
|
|
|
if (NULL_DETECTION_NONSTOP_MODE) {
|
|
|
|
GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
CpuDeadLoop ();
|
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1521
We scan the SMM code with ROPgadget.
http://shell-storm.org/project/ROPgadget/
https://github.com/JonathanSalwan/ROPgadget/tree/master
This tool reports the gadget in SMM driver.
This patch enabled CET ShadowStack for X86 SMM.
If CET is supported, SMM will enable CET ShadowStack.
SMM CET will save the OS CET context at SmmEntry and
restore OS CET context at SmmExit.
Test:
1) test Intel internal platform (x64 only, CET enabled/disabled)
Boot test:
CET supported or not supported CPU
on CET supported platform
CET enabled/disabled
PcdCpuSmmCetEnable enabled/disabled
Single core/Multiple core
PcdCpuSmmStackGuard enabled/disabled
PcdCpuSmmProfileEnable enabled/disabled
PcdCpuSmmStaticPageTable enabled/disabled
CET exception test:
#CF generated with PcdCpuSmmStackGuard enabled/disabled.
Other exception test:
#PF for normal stack overflow
#PF for NX protection
#PF for RO protection
CET env test:
Launch SMM in CET enabled/disabled environment (DXE) - no impact to DXE
The test case can be found at
https://github.com/jyao1/SecurityEx/tree/master/ControlFlowPkg
2) test ovmf (both IA32 and X64 SMM, CET disabled only)
test OvmfIa32/Ovmf3264, with -D SMM_REQUIRE.
qemu-system-x86_64.exe -machine q35,smm=on -smp 4
-serial file:serial.log
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd
QEMU emulator version 3.1.0 (v3.1.0-11736-g7a30e7adb0-dirty)
3) not tested
IA32 CET enabled platform
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yao Jiewen <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
2019-02-22 14:30:36 +01:00
|
|
|
goto Exit;
|
2018-08-20 05:35:58 +02:00
|
|
|
}
|
|
|
|
|
2016-11-24 06:36:56 +01:00
|
|
|
if (IsSmmCommBufferForbiddenAddress (PFAddress)) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));
|
|
|
|
}
|
2015-10-19 21:13:13 +02:00
|
|
|
}
|
|
|
|
|
2024-07-08 06:48:32 +02:00
|
|
|
if (mSmmProfileEnabled) {
|
2015-10-19 21:13:13 +02:00
|
|
|
SmmProfilePFHandler (
|
|
|
|
SystemContext.SystemContextIa32->Eip,
|
|
|
|
SystemContext.SystemContextIa32->ExceptionData
|
|
|
|
);
|
|
|
|
} else {
|
2017-04-01 13:39:22 +02:00
|
|
|
DumpCpuContext (InterruptType, SystemContext);
|
2024-07-31 07:48:47 +02:00
|
|
|
DEBUG_CODE (
|
|
|
|
DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
|
|
|
|
);
|
2024-07-25 07:56:37 +02:00
|
|
|
CpuDeadLoop ();
|
2015-10-19 21:13:13 +02:00
|
|
|
}
|
|
|
|
|
2018-08-20 05:35:58 +02:00
|
|
|
Exit:
|
2016-03-22 03:15:53 +01:00
|
|
|
ReleaseSpinLock (mPFLock);
|
2015-10-19 21:13:13 +02:00
|
|
|
}
|
2016-10-23 17:19:52 +02:00
|
|
|
|
2019-04-01 10:16:01 +02:00
|
|
|
/**
|
|
|
|
This function returns with no action for 32 bit.
|
|
|
|
|
|
|
|
@param[out] *Cr2 Pointer to variable to hold CR2 register value.
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
SaveCr2 (
|
|
|
|
OUT UINTN *Cr2
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
This function returns with no action for 32 bit.
|
|
|
|
|
|
|
|
@param[in] Cr2 Value to write into CR2 register.
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
RestoreCr2 (
|
|
|
|
IN UINTN Cr2
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|