2014-01-21 20:38:25 +01:00
|
|
|
/** @file
|
|
|
|
Main SEC phase code. Transitions to PEI.
|
|
|
|
|
2015-10-16 18:48:24 +02:00
|
|
|
Copyright (c) 2008 - 2015, Intel Corporation. All rights reserved.<BR>
|
2016-07-27 08:48:14 +02:00
|
|
|
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
|
2014-01-21 20:38:25 +01:00
|
|
|
|
|
|
|
This program and the accompanying materials
|
|
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
|
|
which 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 <PiPei.h>
|
|
|
|
|
|
|
|
#include <Library/PeimEntryPoint.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
|
|
#include <Library/DebugLib.h>
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
|
|
#include <Library/PeiServicesLib.h>
|
|
|
|
#include <Library/PcdLib.h>
|
|
|
|
#include <Library/UefiCpuLib.h>
|
|
|
|
#include <Library/DebugAgentLib.h>
|
|
|
|
#include <Library/IoLib.h>
|
|
|
|
#include <Library/PeCoffLib.h>
|
|
|
|
#include <Library/PeCoffGetEntryPointLib.h>
|
|
|
|
#include <Library/PeCoffExtraActionLib.h>
|
|
|
|
#include <Library/ExtractGuidedSectionLib.h>
|
2015-10-16 18:48:24 +02:00
|
|
|
#include <Library/LocalApicLib.h>
|
2014-01-21 20:38:25 +01:00
|
|
|
|
|
|
|
#include <Ppi/TemporaryRamSupport.h>
|
|
|
|
|
|
|
|
#define SEC_IDT_ENTRY_COUNT 34
|
|
|
|
|
|
|
|
typedef struct _SEC_IDT_TABLE {
|
|
|
|
EFI_PEI_SERVICES *PeiService;
|
|
|
|
IA32_IDT_GATE_DESCRIPTOR IdtTable[SEC_IDT_ENTRY_COUNT];
|
|
|
|
} SEC_IDT_TABLE;
|
|
|
|
|
|
|
|
VOID
|
|
|
|
EFIAPI
|
|
|
|
SecStartupPhase2 (
|
|
|
|
IN VOID *Context
|
|
|
|
);
|
|
|
|
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
TemporaryRamMigration (
|
|
|
|
IN CONST EFI_PEI_SERVICES **PeiServices,
|
|
|
|
IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase,
|
|
|
|
IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase,
|
|
|
|
IN UINTN CopySize
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI mTemporaryRamSupportPpi = {
|
|
|
|
TemporaryRamMigration
|
|
|
|
};
|
|
|
|
|
|
|
|
EFI_PEI_PPI_DESCRIPTOR mPrivateDispatchTable[] = {
|
|
|
|
{
|
|
|
|
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
|
|
|
&gEfiTemporaryRamSupportPpiGuid,
|
|
|
|
&mTemporaryRamSupportPpi
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// Template of an IDT entry pointing to 10:FFFFFFE4h.
|
|
|
|
//
|
|
|
|
IA32_IDT_GATE_DESCRIPTOR mIdtEntryTemplate = {
|
|
|
|
{ // Bits
|
|
|
|
0xffe4, // OffsetLow
|
|
|
|
0x10, // Selector
|
|
|
|
0x0, // Reserved_0
|
|
|
|
IA32_IDT_GATE_TYPE_INTERRUPT_32, // GateType
|
|
|
|
0xffff // OffsetHigh
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
Locates the main boot firmware volume.
|
|
|
|
|
|
|
|
@param[in,out] BootFv On input, the base of the BootFv
|
|
|
|
On output, the decompressed main firmware volume
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The main firmware volume was located and decompressed
|
|
|
|
@retval EFI_NOT_FOUND The main firmware volume was not found
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
FindMainFv (
|
|
|
|
IN OUT EFI_FIRMWARE_VOLUME_HEADER **BootFv
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_FIRMWARE_VOLUME_HEADER *Fv;
|
|
|
|
UINTN Distance;
|
|
|
|
|
|
|
|
ASSERT (((UINTN) *BootFv & EFI_PAGE_MASK) == 0);
|
|
|
|
|
|
|
|
Fv = *BootFv;
|
|
|
|
Distance = (UINTN) (*BootFv)->FvLength;
|
|
|
|
do {
|
|
|
|
Fv = (EFI_FIRMWARE_VOLUME_HEADER*) ((UINT8*) Fv - EFI_PAGE_SIZE);
|
|
|
|
Distance += EFI_PAGE_SIZE;
|
|
|
|
if (Distance > SIZE_32MB) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Fv->Signature != EFI_FVH_SIGNATURE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((UINTN) Fv->FvLength > Distance) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
*BootFv = Fv;
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
|
|
|
|
} while (TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Locates a section within a series of sections
|
|
|
|
with the specified section type.
|
|
|
|
|
2014-01-21 20:39:04 +01:00
|
|
|
The Instance parameter indicates which instance of the section
|
|
|
|
type to return. (0 is first instance, 1 is second...)
|
|
|
|
|
2014-01-21 20:38:25 +01:00
|
|
|
@param[in] Sections The sections to search
|
|
|
|
@param[in] SizeOfSections Total size of all sections
|
|
|
|
@param[in] SectionType The section type to locate
|
2014-01-21 20:39:04 +01:00
|
|
|
@param[in] Instance The section instance number
|
2014-01-21 20:38:25 +01:00
|
|
|
@param[out] FoundSection The FFS section if found
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The file and section was found
|
|
|
|
@retval EFI_NOT_FOUND The file and section was not found
|
|
|
|
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
2014-01-21 20:39:04 +01:00
|
|
|
FindFfsSectionInstance (
|
2014-01-21 20:38:25 +01:00
|
|
|
IN VOID *Sections,
|
|
|
|
IN UINTN SizeOfSections,
|
|
|
|
IN EFI_SECTION_TYPE SectionType,
|
2014-01-21 20:39:04 +01:00
|
|
|
IN UINTN Instance,
|
2014-01-21 20:38:25 +01:00
|
|
|
OUT EFI_COMMON_SECTION_HEADER **FoundSection
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_PHYSICAL_ADDRESS CurrentAddress;
|
|
|
|
UINT32 Size;
|
|
|
|
EFI_PHYSICAL_ADDRESS EndOfSections;
|
|
|
|
EFI_COMMON_SECTION_HEADER *Section;
|
|
|
|
EFI_PHYSICAL_ADDRESS EndOfSection;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loop through the FFS file sections within the PEI Core FFS file
|
|
|
|
//
|
|
|
|
EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN) Sections;
|
|
|
|
EndOfSections = EndOfSection + SizeOfSections;
|
|
|
|
for (;;) {
|
|
|
|
if (EndOfSection == EndOfSections) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
CurrentAddress = (EndOfSection + 3) & ~(3ULL);
|
|
|
|
if (CurrentAddress >= EndOfSections) {
|
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
Section = (EFI_COMMON_SECTION_HEADER*)(UINTN) CurrentAddress;
|
|
|
|
|
|
|
|
Size = SECTION_SIZE (Section);
|
|
|
|
if (Size < sizeof (*Section)) {
|
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
EndOfSection = CurrentAddress + Size;
|
|
|
|
if (EndOfSection > EndOfSections) {
|
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Look for the requested section type
|
|
|
|
//
|
|
|
|
if (Section->Type == SectionType) {
|
2014-01-21 20:39:04 +01:00
|
|
|
if (Instance == 0) {
|
|
|
|
*FoundSection = Section;
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Instance--;
|
|
|
|
}
|
2014-01-21 20:38:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
2014-01-21 20:39:04 +01:00
|
|
|
/**
|
|
|
|
Locates a section within a series of sections
|
|
|
|
with the specified section type.
|
|
|
|
|
|
|
|
@param[in] Sections The sections to search
|
|
|
|
@param[in] SizeOfSections Total size of all sections
|
|
|
|
@param[in] SectionType The section type to locate
|
|
|
|
@param[out] FoundSection The FFS section if found
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The file and section was found
|
|
|
|
@retval EFI_NOT_FOUND The file and section was not found
|
|
|
|
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
FindFfsSectionInSections (
|
|
|
|
IN VOID *Sections,
|
|
|
|
IN UINTN SizeOfSections,
|
|
|
|
IN EFI_SECTION_TYPE SectionType,
|
|
|
|
OUT EFI_COMMON_SECTION_HEADER **FoundSection
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return FindFfsSectionInstance (
|
|
|
|
Sections,
|
|
|
|
SizeOfSections,
|
|
|
|
SectionType,
|
|
|
|
0,
|
|
|
|
FoundSection
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-01-21 20:38:25 +01:00
|
|
|
/**
|
|
|
|
Locates a FFS file with the specified file type and a section
|
|
|
|
within that file with the specified section type.
|
|
|
|
|
|
|
|
@param[in] Fv The firmware volume to search
|
|
|
|
@param[in] FileType The file type to locate
|
|
|
|
@param[in] SectionType The section type to locate
|
|
|
|
@param[out] FoundSection The FFS section if found
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The file and section was found
|
|
|
|
@retval EFI_NOT_FOUND The file and section was not found
|
|
|
|
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
FindFfsFileAndSection (
|
|
|
|
IN EFI_FIRMWARE_VOLUME_HEADER *Fv,
|
|
|
|
IN EFI_FV_FILETYPE FileType,
|
|
|
|
IN EFI_SECTION_TYPE SectionType,
|
|
|
|
OUT EFI_COMMON_SECTION_HEADER **FoundSection
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_PHYSICAL_ADDRESS CurrentAddress;
|
|
|
|
EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume;
|
|
|
|
EFI_FFS_FILE_HEADER *File;
|
|
|
|
UINT32 Size;
|
|
|
|
EFI_PHYSICAL_ADDRESS EndOfFile;
|
|
|
|
|
|
|
|
if (Fv->Signature != EFI_FVH_SIGNATURE) {
|
2014-01-21 20:38:50 +01:00
|
|
|
DEBUG ((EFI_D_ERROR, "FV at %p does not have FV header signature\n", Fv));
|
2014-01-21 20:38:25 +01:00
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Fv;
|
|
|
|
EndOfFirmwareVolume = CurrentAddress + Fv->FvLength;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loop through the FFS files in the Boot Firmware Volume
|
|
|
|
//
|
|
|
|
for (EndOfFile = CurrentAddress + Fv->HeaderLength; ; ) {
|
|
|
|
|
|
|
|
CurrentAddress = (EndOfFile + 7) & ~(7ULL);
|
|
|
|
if (CurrentAddress > EndOfFirmwareVolume) {
|
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
File = (EFI_FFS_FILE_HEADER*)(UINTN) CurrentAddress;
|
|
|
|
Size = *(UINT32*) File->Size & 0xffffff;
|
|
|
|
if (Size < (sizeof (*File) + sizeof (EFI_COMMON_SECTION_HEADER))) {
|
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
EndOfFile = CurrentAddress + Size;
|
|
|
|
if (EndOfFile > EndOfFirmwareVolume) {
|
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Look for the request file type
|
|
|
|
//
|
|
|
|
if (File->Type != FileType) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = FindFfsSectionInSections (
|
|
|
|
(VOID*) (File + 1),
|
|
|
|
(UINTN) EndOfFile - (UINTN) (File + 1),
|
|
|
|
SectionType,
|
|
|
|
FoundSection
|
|
|
|
);
|
|
|
|
if (!EFI_ERROR (Status) || (Status == EFI_VOLUME_CORRUPTED)) {
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Locates the compressed main firmware volume and decompresses it.
|
|
|
|
|
|
|
|
@param[in,out] Fv On input, the firmware volume to search
|
2014-01-21 20:39:13 +01:00
|
|
|
On output, the decompressed BOOT/PEI FV
|
2014-01-21 20:38:25 +01:00
|
|
|
|
|
|
|
@retval EFI_SUCCESS The file and section was found
|
|
|
|
@retval EFI_NOT_FOUND The file and section was not found
|
|
|
|
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
2014-01-21 20:39:13 +01:00
|
|
|
DecompressMemFvs (
|
2014-01-21 20:38:25 +01:00
|
|
|
IN OUT EFI_FIRMWARE_VOLUME_HEADER **Fv
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_GUID_DEFINED_SECTION *Section;
|
|
|
|
UINT32 OutputBufferSize;
|
|
|
|
UINT32 ScratchBufferSize;
|
|
|
|
UINT16 SectionAttribute;
|
|
|
|
UINT32 AuthenticationStatus;
|
|
|
|
VOID *OutputBuffer;
|
|
|
|
VOID *ScratchBuffer;
|
2016-07-27 08:48:14 +02:00
|
|
|
EFI_COMMON_SECTION_HEADER *FvSection;
|
2014-01-21 20:39:13 +01:00
|
|
|
EFI_FIRMWARE_VOLUME_HEADER *PeiMemFv;
|
|
|
|
EFI_FIRMWARE_VOLUME_HEADER *DxeMemFv;
|
2016-07-27 08:48:15 +02:00
|
|
|
UINT32 FvHeaderSize;
|
|
|
|
UINT32 FvSectionSize;
|
2014-01-21 20:38:25 +01:00
|
|
|
|
2016-07-27 08:48:14 +02:00
|
|
|
FvSection = (EFI_COMMON_SECTION_HEADER*) NULL;
|
2014-01-21 20:38:25 +01:00
|
|
|
|
|
|
|
Status = FindFfsFileAndSection (
|
|
|
|
*Fv,
|
|
|
|
EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE,
|
|
|
|
EFI_SECTION_GUID_DEFINED,
|
|
|
|
(EFI_COMMON_SECTION_HEADER**) &Section
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((EFI_D_ERROR, "Unable to find GUID defined section\n"));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = ExtractGuidedSectionGetInfo (
|
|
|
|
Section,
|
|
|
|
&OutputBufferSize,
|
|
|
|
&ScratchBufferSize,
|
|
|
|
&SectionAttribute
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((EFI_D_ERROR, "Unable to GetInfo for GUIDed section\n"));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2014-01-21 20:39:13 +01:00
|
|
|
OutputBuffer = (VOID*) ((UINT8*)(UINTN) PcdGet32 (PcdOvmfDxeMemFvBase) + SIZE_1MB);
|
2014-01-21 20:38:25 +01:00
|
|
|
ScratchBuffer = ALIGN_POINTER ((UINT8*) OutputBuffer + OutputBufferSize, SIZE_1MB);
|
OvmfPkg: Sec: assert the build-time calculated end of the scratch buffer
The DecompressMemFvs() function in "OvmfPkg/Sec/SecMain.c" uses more
memory, temporarily, than what PEIFV and DXEFV will ultimately need.
First, it uses an output buffer for decompression, second, the
decompression itself needs a scratch buffer (and this scratch buffer is
the highest area that SEC uses).
DecompressMemFvs() used to be called on normal boots only (ie. not on S3
resume), which is why the decompression output buffer and the scratch
buffer were allowed to scribble over RAM. However, we'll soon start to
worry during S3 resume that the runtime OS might tamper with the
pre-decompressed PEIFV, and we'll decompress the firmware volumes on S3
resume too, from pristine flash. For this we'll need to know the end of
the scratch buffer in advance, so we can prepare a non-malicious OS for
it.
Calculate the end of the scratch buffer statically in the FDF files, and
assert in DecompressMemFvs() that the runtime decompression will match it.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19036 6f19259b-4bc3-4df7-8a09-765794883524
2015-11-30 19:41:20 +01:00
|
|
|
|
|
|
|
DEBUG ((EFI_D_VERBOSE, "%a: OutputBuffer@%p+0x%x ScratchBuffer@%p+0x%x "
|
|
|
|
"PcdOvmfDecompressionScratchEnd=0x%x\n", __FUNCTION__, OutputBuffer,
|
|
|
|
OutputBufferSize, ScratchBuffer, ScratchBufferSize,
|
|
|
|
PcdGet32 (PcdOvmfDecompressionScratchEnd)));
|
|
|
|
ASSERT ((UINTN)ScratchBuffer + ScratchBufferSize ==
|
|
|
|
PcdGet32 (PcdOvmfDecompressionScratchEnd));
|
|
|
|
|
2014-01-21 20:38:25 +01:00
|
|
|
Status = ExtractGuidedSectionDecode (
|
|
|
|
Section,
|
|
|
|
&OutputBuffer,
|
|
|
|
ScratchBuffer,
|
|
|
|
&AuthenticationStatus
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((EFI_D_ERROR, "Error during GUID section decode\n"));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2014-01-21 20:39:13 +01:00
|
|
|
Status = FindFfsSectionInstance (
|
2014-01-21 20:38:25 +01:00
|
|
|
OutputBuffer,
|
|
|
|
OutputBufferSize,
|
|
|
|
EFI_SECTION_FIRMWARE_VOLUME_IMAGE,
|
2014-01-21 20:39:13 +01:00
|
|
|
0,
|
2016-07-27 08:48:14 +02:00
|
|
|
&FvSection
|
2014-01-21 20:38:25 +01:00
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2014-01-21 20:39:13 +01:00
|
|
|
DEBUG ((EFI_D_ERROR, "Unable to find PEI FV section\n"));
|
2014-01-21 20:38:25 +01:00
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2014-01-21 20:39:13 +01:00
|
|
|
ASSERT (SECTION_SIZE (FvSection) ==
|
|
|
|
(PcdGet32 (PcdOvmfPeiMemFvSize) + sizeof (*FvSection)));
|
|
|
|
ASSERT (FvSection->Type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE);
|
2014-01-21 20:38:25 +01:00
|
|
|
|
2014-01-21 20:39:13 +01:00
|
|
|
PeiMemFv = (EFI_FIRMWARE_VOLUME_HEADER*)(UINTN) PcdGet32 (PcdOvmfPeiMemFvBase);
|
|
|
|
CopyMem (PeiMemFv, (VOID*) (FvSection + 1), PcdGet32 (PcdOvmfPeiMemFvSize));
|
|
|
|
|
|
|
|
if (PeiMemFv->Signature != EFI_FVH_SIGNATURE) {
|
|
|
|
DEBUG ((EFI_D_ERROR, "Extracted FV at %p does not have FV header signature\n", PeiMemFv));
|
|
|
|
CpuDeadLoop ();
|
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = FindFfsSectionInstance (
|
|
|
|
OutputBuffer,
|
|
|
|
OutputBufferSize,
|
|
|
|
EFI_SECTION_FIRMWARE_VOLUME_IMAGE,
|
|
|
|
1,
|
2016-07-27 08:48:14 +02:00
|
|
|
&FvSection
|
2014-01-21 20:39:13 +01:00
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((EFI_D_ERROR, "Unable to find DXE FV section\n"));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT (FvSection->Type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE);
|
2016-07-27 08:48:15 +02:00
|
|
|
|
|
|
|
if (IS_SECTION2 (FvSection)) {
|
|
|
|
FvSectionSize = SECTION2_SIZE (FvSection);
|
|
|
|
FvHeaderSize = sizeof (EFI_COMMON_SECTION_HEADER2);
|
|
|
|
} else {
|
|
|
|
FvSectionSize = SECTION_SIZE (FvSection);
|
|
|
|
FvHeaderSize = sizeof (EFI_COMMON_SECTION_HEADER);
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT (FvSectionSize == (PcdGet32 (PcdOvmfDxeMemFvSize) + FvHeaderSize));
|
2014-01-21 20:39:13 +01:00
|
|
|
|
|
|
|
DxeMemFv = (EFI_FIRMWARE_VOLUME_HEADER*)(UINTN) PcdGet32 (PcdOvmfDxeMemFvBase);
|
2016-07-27 08:48:15 +02:00
|
|
|
CopyMem (DxeMemFv, (VOID*) ((UINTN)FvSection + FvHeaderSize), PcdGet32 (PcdOvmfDxeMemFvSize));
|
2014-01-21 20:39:13 +01:00
|
|
|
|
|
|
|
if (DxeMemFv->Signature != EFI_FVH_SIGNATURE) {
|
|
|
|
DEBUG ((EFI_D_ERROR, "Extracted FV at %p does not have FV header signature\n", DxeMemFv));
|
2014-01-21 20:38:25 +01:00
|
|
|
CpuDeadLoop ();
|
|
|
|
return EFI_VOLUME_CORRUPTED;
|
|
|
|
}
|
|
|
|
|
2014-01-21 20:39:13 +01:00
|
|
|
*Fv = PeiMemFv;
|
2014-01-21 20:38:25 +01:00
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Locates the PEI Core entry point address
|
|
|
|
|
|
|
|
@param[in] Fv The firmware volume to search
|
|
|
|
@param[out] PeiCoreEntryPoint The entry point of the PEI Core image
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The file and section was found
|
|
|
|
@retval EFI_NOT_FOUND The file and section was not found
|
|
|
|
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
FindPeiCoreImageBaseInFv (
|
|
|
|
IN EFI_FIRMWARE_VOLUME_HEADER *Fv,
|
|
|
|
OUT EFI_PHYSICAL_ADDRESS *PeiCoreImageBase
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_COMMON_SECTION_HEADER *Section;
|
|
|
|
|
|
|
|
Status = FindFfsFileAndSection (
|
|
|
|
Fv,
|
|
|
|
EFI_FV_FILETYPE_PEI_CORE,
|
|
|
|
EFI_SECTION_PE32,
|
|
|
|
&Section
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
Status = FindFfsFileAndSection (
|
|
|
|
Fv,
|
|
|
|
EFI_FV_FILETYPE_PEI_CORE,
|
|
|
|
EFI_SECTION_TE,
|
|
|
|
&Section
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((EFI_D_ERROR, "Unable to find PEI Core image\n"));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*PeiCoreImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)(Section + 1);
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-03-04 09:02:37 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
Reads 8-bits of CMOS data.
|
|
|
|
|
|
|
|
Reads the 8-bits of CMOS data at the location specified by Index.
|
|
|
|
The 8-bit read value is returned.
|
|
|
|
|
|
|
|
@param Index The CMOS location to read.
|
|
|
|
|
|
|
|
@return The value read.
|
|
|
|
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
UINT8
|
|
|
|
CmosRead8 (
|
|
|
|
IN UINTN Index
|
|
|
|
)
|
|
|
|
{
|
|
|
|
IoWrite8 (0x70, (UINT8) Index);
|
|
|
|
return IoRead8 (0x71);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
BOOLEAN
|
|
|
|
IsS3Resume (
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return (CmosRead8 (0xF) == 0xFE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
EFI_STATUS
|
|
|
|
GetS3ResumePeiFv (
|
|
|
|
IN OUT EFI_FIRMWARE_VOLUME_HEADER **PeiFv
|
|
|
|
)
|
|
|
|
{
|
|
|
|
*PeiFv = (EFI_FIRMWARE_VOLUME_HEADER*)(UINTN) PcdGet32 (PcdOvmfPeiMemFvBase);
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-21 20:38:25 +01:00
|
|
|
/**
|
|
|
|
Locates the PEI Core entry point address
|
|
|
|
|
|
|
|
@param[in,out] Fv The firmware volume to search
|
|
|
|
@param[out] PeiCoreEntryPoint The entry point of the PEI Core image
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The file and section was found
|
|
|
|
@retval EFI_NOT_FOUND The file and section was not found
|
|
|
|
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
|
|
|
|
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
FindPeiCoreImageBase (
|
|
|
|
IN OUT EFI_FIRMWARE_VOLUME_HEADER **BootFv,
|
|
|
|
OUT EFI_PHYSICAL_ADDRESS *PeiCoreImageBase
|
|
|
|
)
|
|
|
|
{
|
OvmfPkg: decompress FVs on S3 resume if SMM_REQUIRE is set
If OVMF was built with -D SMM_REQUIRE, that implies that the runtime OS is
not trusted and we should defend against it tampering with the firmware's
data.
One such datum is the PEI firmware volume (PEIFV). Normally PEIFV is
decompressed on the first boot by SEC, then the OS preserves it across S3
suspend-resume cycles; at S3 resume SEC just reuses the originally
decompressed PEIFV.
However, if we don't trust the OS, then SEC must decompress PEIFV from the
pristine flash every time, lest we execute OS-injected code or work with
OS-injected data.
Due to how FVMAIN_COMPACT is organized, we can't decompress just PEIFV;
the decompression brings DXEFV with itself, plus it uses a temporary
output buffer and a scratch buffer too, which even reach above the end of
the finally installed DXEFV. For this reason we must keep away a
non-malicious OS from DXEFV too, plus the memory up to
PcdOvmfDecomprScratchEnd.
The delay introduced by the LZMA decompression on S3 resume is negligible.
If -D SMM_REQUIRE is not specified, then PcdSmmSmramRequire remains FALSE
(from the DEC file), and then this patch has no effect (not counting some
changed debug messages).
If QEMU doesn't support S3 (or the user disabled it on the QEMU command
line), then this patch has no effect also.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19037 6f19259b-4bc3-4df7-8a09-765794883524
2015-11-30 19:41:24 +01:00
|
|
|
BOOLEAN S3Resume;
|
|
|
|
|
2014-01-21 20:38:25 +01:00
|
|
|
*PeiCoreImageBase = 0;
|
|
|
|
|
OvmfPkg: decompress FVs on S3 resume if SMM_REQUIRE is set
If OVMF was built with -D SMM_REQUIRE, that implies that the runtime OS is
not trusted and we should defend against it tampering with the firmware's
data.
One such datum is the PEI firmware volume (PEIFV). Normally PEIFV is
decompressed on the first boot by SEC, then the OS preserves it across S3
suspend-resume cycles; at S3 resume SEC just reuses the originally
decompressed PEIFV.
However, if we don't trust the OS, then SEC must decompress PEIFV from the
pristine flash every time, lest we execute OS-injected code or work with
OS-injected data.
Due to how FVMAIN_COMPACT is organized, we can't decompress just PEIFV;
the decompression brings DXEFV with itself, plus it uses a temporary
output buffer and a scratch buffer too, which even reach above the end of
the finally installed DXEFV. For this reason we must keep away a
non-malicious OS from DXEFV too, plus the memory up to
PcdOvmfDecomprScratchEnd.
The delay introduced by the LZMA decompression on S3 resume is negligible.
If -D SMM_REQUIRE is not specified, then PcdSmmSmramRequire remains FALSE
(from the DEC file), and then this patch has no effect (not counting some
changed debug messages).
If QEMU doesn't support S3 (or the user disabled it on the QEMU command
line), then this patch has no effect also.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19037 6f19259b-4bc3-4df7-8a09-765794883524
2015-11-30 19:41:24 +01:00
|
|
|
S3Resume = IsS3Resume ();
|
|
|
|
if (S3Resume && !FeaturePcdGet (PcdSmmSmramRequire)) {
|
|
|
|
//
|
|
|
|
// A malicious runtime OS may have injected something into our previously
|
|
|
|
// decoded PEI FV, but we don't care about that unless SMM/SMRAM is required.
|
|
|
|
//
|
2014-03-04 09:02:37 +01:00
|
|
|
DEBUG ((EFI_D_VERBOSE, "SEC: S3 resume\n"));
|
|
|
|
GetS3ResumePeiFv (BootFv);
|
|
|
|
} else {
|
OvmfPkg: decompress FVs on S3 resume if SMM_REQUIRE is set
If OVMF was built with -D SMM_REQUIRE, that implies that the runtime OS is
not trusted and we should defend against it tampering with the firmware's
data.
One such datum is the PEI firmware volume (PEIFV). Normally PEIFV is
decompressed on the first boot by SEC, then the OS preserves it across S3
suspend-resume cycles; at S3 resume SEC just reuses the originally
decompressed PEIFV.
However, if we don't trust the OS, then SEC must decompress PEIFV from the
pristine flash every time, lest we execute OS-injected code or work with
OS-injected data.
Due to how FVMAIN_COMPACT is organized, we can't decompress just PEIFV;
the decompression brings DXEFV with itself, plus it uses a temporary
output buffer and a scratch buffer too, which even reach above the end of
the finally installed DXEFV. For this reason we must keep away a
non-malicious OS from DXEFV too, plus the memory up to
PcdOvmfDecomprScratchEnd.
The delay introduced by the LZMA decompression on S3 resume is negligible.
If -D SMM_REQUIRE is not specified, then PcdSmmSmramRequire remains FALSE
(from the DEC file), and then this patch has no effect (not counting some
changed debug messages).
If QEMU doesn't support S3 (or the user disabled it on the QEMU command
line), then this patch has no effect also.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19037 6f19259b-4bc3-4df7-8a09-765794883524
2015-11-30 19:41:24 +01:00
|
|
|
//
|
|
|
|
// We're either not resuming, or resuming "securely" -- we'll decompress
|
|
|
|
// both PEI FV and DXE FV from pristine flash.
|
|
|
|
//
|
|
|
|
DEBUG ((EFI_D_VERBOSE, "SEC: %a\n",
|
|
|
|
S3Resume ? "S3 resume (with PEI decompression)" : "Normal boot"));
|
2014-03-04 09:02:37 +01:00
|
|
|
FindMainFv (BootFv);
|
2014-01-21 20:38:25 +01:00
|
|
|
|
2014-03-04 09:02:37 +01:00
|
|
|
DecompressMemFvs (BootFv);
|
|
|
|
}
|
2014-01-21 20:38:25 +01:00
|
|
|
|
|
|
|
FindPeiCoreImageBaseInFv (*BootFv, PeiCoreImageBase);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Find core image base.
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
FindImageBase (
|
|
|
|
IN EFI_FIRMWARE_VOLUME_HEADER *BootFirmwareVolumePtr,
|
|
|
|
OUT EFI_PHYSICAL_ADDRESS *SecCoreImageBase
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_PHYSICAL_ADDRESS CurrentAddress;
|
|
|
|
EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume;
|
|
|
|
EFI_FFS_FILE_HEADER *File;
|
|
|
|
UINT32 Size;
|
|
|
|
EFI_PHYSICAL_ADDRESS EndOfFile;
|
|
|
|
EFI_COMMON_SECTION_HEADER *Section;
|
|
|
|
EFI_PHYSICAL_ADDRESS EndOfSection;
|
|
|
|
|
|
|
|
*SecCoreImageBase = 0;
|
|
|
|
|
|
|
|
CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) BootFirmwareVolumePtr;
|
|
|
|
EndOfFirmwareVolume = CurrentAddress + BootFirmwareVolumePtr->FvLength;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loop through the FFS files in the Boot Firmware Volume
|
|
|
|
//
|
|
|
|
for (EndOfFile = CurrentAddress + BootFirmwareVolumePtr->HeaderLength; ; ) {
|
|
|
|
|
|
|
|
CurrentAddress = (EndOfFile + 7) & 0xfffffffffffffff8ULL;
|
|
|
|
if (CurrentAddress > EndOfFirmwareVolume) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
File = (EFI_FFS_FILE_HEADER*)(UINTN) CurrentAddress;
|
|
|
|
Size = *(UINT32*) File->Size & 0xffffff;
|
|
|
|
if (Size < sizeof (*File)) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
EndOfFile = CurrentAddress + Size;
|
|
|
|
if (EndOfFile > EndOfFirmwareVolume) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Look for SEC Core
|
|
|
|
//
|
|
|
|
if (File->Type != EFI_FV_FILETYPE_SECURITY_CORE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loop through the FFS file sections within the FFS file
|
|
|
|
//
|
|
|
|
EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN) (File + 1);
|
|
|
|
for (;;) {
|
|
|
|
CurrentAddress = (EndOfSection + 3) & 0xfffffffffffffffcULL;
|
|
|
|
Section = (EFI_COMMON_SECTION_HEADER*)(UINTN) CurrentAddress;
|
|
|
|
|
|
|
|
Size = *(UINT32*) Section->Size & 0xffffff;
|
|
|
|
if (Size < sizeof (*Section)) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
EndOfSection = CurrentAddress + Size;
|
|
|
|
if (EndOfSection > EndOfFile) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Look for executable sections
|
|
|
|
//
|
|
|
|
if (Section->Type == EFI_SECTION_PE32 || Section->Type == EFI_SECTION_TE) {
|
|
|
|
if (File->Type == EFI_FV_FILETYPE_SECURITY_CORE) {
|
|
|
|
*SecCoreImageBase = (PHYSICAL_ADDRESS) (UINTN) (Section + 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// SEC Core image found
|
|
|
|
//
|
|
|
|
if (*SecCoreImageBase != 0) {
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Find and return Pei Core entry point.
|
|
|
|
|
2016-10-19 09:01:31 +02:00
|
|
|
It also find SEC and PEI Core file debug information. It will report them if
|
2014-01-21 20:38:25 +01:00
|
|
|
remote debug is enabled.
|
|
|
|
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
FindAndReportEntryPoints (
|
|
|
|
IN EFI_FIRMWARE_VOLUME_HEADER **BootFirmwareVolumePtr,
|
|
|
|
OUT EFI_PEI_CORE_ENTRY_POINT *PeiCoreEntryPoint
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_PHYSICAL_ADDRESS SecCoreImageBase;
|
|
|
|
EFI_PHYSICAL_ADDRESS PeiCoreImageBase;
|
|
|
|
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Find SEC Core and PEI Core image base
|
|
|
|
//
|
|
|
|
Status = FindImageBase (*BootFirmwareVolumePtr, &SecCoreImageBase);
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
|
|
|
|
FindPeiCoreImageBase (BootFirmwareVolumePtr, &PeiCoreImageBase);
|
|
|
|
|
|
|
|
ZeroMem ((VOID *) &ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
|
|
|
|
//
|
|
|
|
// Report SEC Core debug information when remote debug is enabled
|
|
|
|
//
|
|
|
|
ImageContext.ImageAddress = SecCoreImageBase;
|
|
|
|
ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageContext.ImageAddress);
|
|
|
|
PeCoffLoaderRelocateImageExtraAction (&ImageContext);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Report PEI Core debug information when remote debug is enabled
|
|
|
|
//
|
|
|
|
ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiCoreImageBase;
|
|
|
|
ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageContext.ImageAddress);
|
|
|
|
PeCoffLoaderRelocateImageExtraAction (&ImageContext);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Find PEI Core entry point
|
|
|
|
//
|
|
|
|
Status = PeCoffLoaderGetEntryPoint ((VOID *) (UINTN) PeiCoreImageBase, (VOID**) PeiCoreEntryPoint);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
*PeiCoreEntryPoint = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
EFIAPI
|
|
|
|
SecCoreStartupWithStack (
|
|
|
|
IN EFI_FIRMWARE_VOLUME_HEADER *BootFv,
|
|
|
|
IN VOID *TopOfCurrentStack
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_SEC_PEI_HAND_OFF SecCoreData;
|
|
|
|
SEC_IDT_TABLE IdtTableInStack;
|
|
|
|
IA32_DESCRIPTOR IdtDescriptor;
|
|
|
|
UINT32 Index;
|
2015-11-30 19:41:14 +01:00
|
|
|
volatile UINT8 *Table;
|
|
|
|
|
|
|
|
//
|
|
|
|
// To ensure SMM can't be compromised on S3 resume, we must force re-init of
|
|
|
|
// the BaseExtractGuidedSectionLib. Since this is before library contructors
|
|
|
|
// are called, we must use a loop rather than SetMem.
|
|
|
|
//
|
|
|
|
Table = (UINT8*)(UINTN)FixedPcdGet64 (PcdGuidedExtractHandlerTableAddress);
|
|
|
|
for (Index = 0;
|
|
|
|
Index < FixedPcdGet32 (PcdGuidedExtractHandlerTableSize);
|
|
|
|
++Index) {
|
|
|
|
Table[Index] = 0;
|
|
|
|
}
|
2014-01-21 20:38:25 +01:00
|
|
|
|
|
|
|
ProcessLibraryConstructorList (NULL, NULL);
|
|
|
|
|
2014-01-21 20:38:50 +01:00
|
|
|
DEBUG ((EFI_D_INFO,
|
2014-01-21 20:38:25 +01:00
|
|
|
"SecCoreStartupWithStack(0x%x, 0x%x)\n",
|
|
|
|
(UINT32)(UINTN)BootFv,
|
|
|
|
(UINT32)(UINTN)TopOfCurrentStack
|
|
|
|
));
|
|
|
|
|
|
|
|
//
|
|
|
|
// Initialize floating point operating environment
|
|
|
|
// to be compliant with UEFI spec.
|
|
|
|
//
|
|
|
|
InitializeFloatingPointUnits ();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Initialize IDT
|
|
|
|
//
|
|
|
|
IdtTableInStack.PeiService = NULL;
|
|
|
|
for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {
|
|
|
|
CopyMem (&IdtTableInStack.IdtTable[Index], &mIdtEntryTemplate, sizeof (mIdtEntryTemplate));
|
|
|
|
}
|
|
|
|
|
|
|
|
IdtDescriptor.Base = (UINTN)&IdtTableInStack.IdtTable;
|
|
|
|
IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1);
|
|
|
|
|
|
|
|
AsmWriteIdtr (&IdtDescriptor);
|
|
|
|
|
2014-01-21 20:38:34 +01:00
|
|
|
#if defined (MDE_CPU_X64)
|
|
|
|
//
|
|
|
|
// ASSERT that the Page Tables were set by the reset vector code to
|
|
|
|
// the address we expect.
|
|
|
|
//
|
|
|
|
ASSERT (AsmReadCr3 () == (UINTN) PcdGet32 (PcdOvmfSecPageTablesBase));
|
|
|
|
#endif
|
|
|
|
|
2014-01-21 20:38:25 +01:00
|
|
|
//
|
|
|
|
// |-------------| <-- TopOfCurrentStack
|
|
|
|
// | Stack | 32k
|
|
|
|
// |-------------|
|
|
|
|
// | Heap | 32k
|
|
|
|
// |-------------| <-- SecCoreData.TemporaryRamBase
|
|
|
|
//
|
|
|
|
|
2014-01-21 20:38:43 +01:00
|
|
|
ASSERT ((UINTN) (PcdGet32 (PcdOvmfSecPeiTempRamBase) +
|
|
|
|
PcdGet32 (PcdOvmfSecPeiTempRamSize)) ==
|
|
|
|
(UINTN) TopOfCurrentStack);
|
|
|
|
|
2014-01-21 20:38:25 +01:00
|
|
|
//
|
|
|
|
// Initialize SEC hand-off state
|
|
|
|
//
|
|
|
|
SecCoreData.DataSize = sizeof(EFI_SEC_PEI_HAND_OFF);
|
|
|
|
|
2014-01-21 20:38:43 +01:00
|
|
|
SecCoreData.TemporaryRamSize = (UINTN) PcdGet32 (PcdOvmfSecPeiTempRamSize);
|
2014-01-21 20:38:25 +01:00
|
|
|
SecCoreData.TemporaryRamBase = (VOID*)((UINT8 *)TopOfCurrentStack - SecCoreData.TemporaryRamSize);
|
|
|
|
|
|
|
|
SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase;
|
|
|
|
SecCoreData.PeiTemporaryRamSize = SecCoreData.TemporaryRamSize >> 1;
|
|
|
|
|
|
|
|
SecCoreData.StackBase = (UINT8 *)SecCoreData.TemporaryRamBase + SecCoreData.PeiTemporaryRamSize;
|
|
|
|
SecCoreData.StackSize = SecCoreData.TemporaryRamSize >> 1;
|
|
|
|
|
|
|
|
SecCoreData.BootFirmwareVolumeBase = BootFv;
|
|
|
|
SecCoreData.BootFirmwareVolumeSize = (UINTN) BootFv->FvLength;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Make sure the 8259 is masked before initializing the Debug Agent and the debug timer is enabled
|
|
|
|
//
|
|
|
|
IoWrite8 (0x21, 0xff);
|
|
|
|
IoWrite8 (0xA1, 0xff);
|
2015-10-16 18:48:24 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Initialize Local APIC Timer hardware and disable Local APIC Timer
|
|
|
|
// interrupts before initializing the Debug Agent and the debug timer is
|
|
|
|
// enabled.
|
|
|
|
//
|
|
|
|
InitializeApicTimer (0, MAX_UINT32, TRUE, 5);
|
|
|
|
DisableApicTimerInterrupt ();
|
2014-01-21 20:38:25 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready.
|
|
|
|
//
|
|
|
|
InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, &SecCoreData, SecStartupPhase2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Caller provided function to be invoked at the end of InitializeDebugAgent().
|
|
|
|
|
|
|
|
Entry point to the C language phase of SEC. After the SEC assembly
|
|
|
|
code has initialized some temporary memory and set up the stack,
|
|
|
|
the control is transferred to this function.
|
|
|
|
|
|
|
|
@param[in] Context The first input parameter of InitializeDebugAgent().
|
|
|
|
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
EFIAPI
|
|
|
|
SecStartupPhase2(
|
|
|
|
IN VOID *Context
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_SEC_PEI_HAND_OFF *SecCoreData;
|
|
|
|
EFI_FIRMWARE_VOLUME_HEADER *BootFv;
|
|
|
|
EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint;
|
|
|
|
|
|
|
|
SecCoreData = (EFI_SEC_PEI_HAND_OFF *) Context;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
|
|
|
|
// is enabled.
|
|
|
|
//
|
|
|
|
BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
|
|
|
|
FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
|
|
|
|
SecCoreData->BootFirmwareVolumeBase = BootFv;
|
|
|
|
SecCoreData->BootFirmwareVolumeSize = (UINTN) BootFv->FvLength;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Transfer the control to the PEI core
|
|
|
|
//
|
|
|
|
(*PeiCoreEntryPoint) (SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);
|
|
|
|
|
|
|
|
//
|
|
|
|
// If we get here then the PEI Core returned, which is not recoverable.
|
|
|
|
//
|
|
|
|
ASSERT (FALSE);
|
|
|
|
CpuDeadLoop ();
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
TemporaryRamMigration (
|
|
|
|
IN CONST EFI_PEI_SERVICES **PeiServices,
|
|
|
|
IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase,
|
|
|
|
IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase,
|
|
|
|
IN UINTN CopySize
|
|
|
|
)
|
|
|
|
{
|
|
|
|
IA32_DESCRIPTOR IdtDescriptor;
|
|
|
|
VOID *OldHeap;
|
|
|
|
VOID *NewHeap;
|
|
|
|
VOID *OldStack;
|
|
|
|
VOID *NewStack;
|
|
|
|
DEBUG_AGENT_CONTEXT_POSTMEM_SEC DebugAgentContext;
|
|
|
|
BOOLEAN OldStatus;
|
|
|
|
BASE_LIBRARY_JUMP_BUFFER JumpBuffer;
|
|
|
|
|
2014-01-21 20:38:50 +01:00
|
|
|
DEBUG ((EFI_D_INFO,
|
2015-07-28 20:33:23 +02:00
|
|
|
"TemporaryRamMigration(0x%Lx, 0x%Lx, 0x%Lx)\n",
|
|
|
|
TemporaryMemoryBase,
|
|
|
|
PermanentMemoryBase,
|
|
|
|
(UINT64)CopySize
|
2014-01-21 20:38:50 +01:00
|
|
|
));
|
2014-01-21 20:38:25 +01:00
|
|
|
|
|
|
|
OldHeap = (VOID*)(UINTN)TemporaryMemoryBase;
|
|
|
|
NewHeap = (VOID*)((UINTN)PermanentMemoryBase + (CopySize >> 1));
|
|
|
|
|
|
|
|
OldStack = (VOID*)((UINTN)TemporaryMemoryBase + (CopySize >> 1));
|
|
|
|
NewStack = (VOID*)(UINTN)PermanentMemoryBase;
|
|
|
|
|
|
|
|
DebugAgentContext.HeapMigrateOffset = (UINTN)NewHeap - (UINTN)OldHeap;
|
|
|
|
DebugAgentContext.StackMigrateOffset = (UINTN)NewStack - (UINTN)OldStack;
|
|
|
|
|
|
|
|
OldStatus = SaveAndSetDebugTimerInterrupt (FALSE);
|
|
|
|
InitializeDebugAgent (DEBUG_AGENT_INIT_POSTMEM_SEC, (VOID *) &DebugAgentContext, NULL);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Migrate Heap
|
|
|
|
//
|
|
|
|
CopyMem (NewHeap, OldHeap, CopySize >> 1);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Migrate Stack
|
|
|
|
//
|
|
|
|
CopyMem (NewStack, OldStack, CopySize >> 1);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Rebase IDT table in permanent memory
|
|
|
|
//
|
|
|
|
AsmReadIdtr (&IdtDescriptor);
|
|
|
|
IdtDescriptor.Base = IdtDescriptor.Base - (UINTN)OldStack + (UINTN)NewStack;
|
|
|
|
|
|
|
|
AsmWriteIdtr (&IdtDescriptor);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Use SetJump()/LongJump() to switch to a new stack.
|
|
|
|
//
|
|
|
|
if (SetJump (&JumpBuffer) == 0) {
|
|
|
|
#if defined (MDE_CPU_IA32)
|
|
|
|
JumpBuffer.Esp = JumpBuffer.Esp + DebugAgentContext.StackMigrateOffset;
|
OvmfPkg/SecMain: Fix stack switching to permanent memory
In earlier PEI stage, temporary memory at PcdOvmfSecPeiTempRamBase is
employed as stack and heap. We move them to the new room and do some
relocation fixup when permanent memory becomes available.
TemporaryRamMigration() is responsible for switching the stack.
Before entering TemporaryRamMigration(), Ebp/Rbp is populated with the
content of Esp/Rsp and used as frame pointer.
After the execution of SetJump/LongJump, stack migrates to new position
while the context keeps unchanged.
But when TemporaryRamMigration() exits, Esp/Rsp is filled with
the content of Ebp/Rbp to destroy this stack frame.
The result is, stack switches back to previous temporary memory.
When permanent memory becomes available, modules that have registered
themselves for shadowing will be scheduled to execute. Some of them
need to consume more memory(heap/stack). Contrast to temporary stack,
permanent stack possesses larger space.
The potential risk is overflowing the stack if stack staying in
temporary memory. When it happens, system may crash during S3 resume.
More detailed information:
> (gdb) disassemble /r
> Dump of assembler code for function TemporaryRamMigration:
> 0x00000000fffcd29c <+0>: 55 push %rbp
> 0x00000000fffcd29d <+1>: 48 89 e5 mov %rsp,%rbp
> 0x00000000fffcd2a0 <+4>: 48 81 ec 70 01 00 00 sub
> $0x170,%rsp
> ...
> ...
> 0x00000000fffcd425 <+393>: e8 80 10 00 00 callq 0xfffce4aa
> <SaveAndSetDebugTimerInterrupt>
> => 0x00000000fffcd42a <+398>: b8 00 00 00 00 mov $0x0,%eax
> 0x00000000fffcd42f <+403>: c9 leaveq
> 0x00000000fffcd430 <+404>: c3 retq
> End of assembler dump.
See the description of leave(opcode: c9), from
Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2A
"Releases the stack frame set up by an earlier ENTER instruction. The
LEAVE instruction copies the frame pointer (in the EBP register) into
the stack pointer register (ESP), which releases the stack space
allocated to the stack frame. The old frame pointer (the frame pointer
for the calling procedure that was saved by the ENTER instruction) is
then popped from the stack into the EBP register, restoring the calling
procedure’s stack frame."
To solve this, update Ebp/Rbp too when Esp/Rsp is updated
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Ge Song <ge.song@hxt-semitech.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-09-06 05:11:35 +02:00
|
|
|
JumpBuffer.Ebp = JumpBuffer.Ebp + DebugAgentContext.StackMigrateOffset;
|
2014-01-21 20:38:25 +01:00
|
|
|
#endif
|
|
|
|
#if defined (MDE_CPU_X64)
|
|
|
|
JumpBuffer.Rsp = JumpBuffer.Rsp + DebugAgentContext.StackMigrateOffset;
|
OvmfPkg/SecMain: Fix stack switching to permanent memory
In earlier PEI stage, temporary memory at PcdOvmfSecPeiTempRamBase is
employed as stack and heap. We move them to the new room and do some
relocation fixup when permanent memory becomes available.
TemporaryRamMigration() is responsible for switching the stack.
Before entering TemporaryRamMigration(), Ebp/Rbp is populated with the
content of Esp/Rsp and used as frame pointer.
After the execution of SetJump/LongJump, stack migrates to new position
while the context keeps unchanged.
But when TemporaryRamMigration() exits, Esp/Rsp is filled with
the content of Ebp/Rbp to destroy this stack frame.
The result is, stack switches back to previous temporary memory.
When permanent memory becomes available, modules that have registered
themselves for shadowing will be scheduled to execute. Some of them
need to consume more memory(heap/stack). Contrast to temporary stack,
permanent stack possesses larger space.
The potential risk is overflowing the stack if stack staying in
temporary memory. When it happens, system may crash during S3 resume.
More detailed information:
> (gdb) disassemble /r
> Dump of assembler code for function TemporaryRamMigration:
> 0x00000000fffcd29c <+0>: 55 push %rbp
> 0x00000000fffcd29d <+1>: 48 89 e5 mov %rsp,%rbp
> 0x00000000fffcd2a0 <+4>: 48 81 ec 70 01 00 00 sub
> $0x170,%rsp
> ...
> ...
> 0x00000000fffcd425 <+393>: e8 80 10 00 00 callq 0xfffce4aa
> <SaveAndSetDebugTimerInterrupt>
> => 0x00000000fffcd42a <+398>: b8 00 00 00 00 mov $0x0,%eax
> 0x00000000fffcd42f <+403>: c9 leaveq
> 0x00000000fffcd430 <+404>: c3 retq
> End of assembler dump.
See the description of leave(opcode: c9), from
Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2A
"Releases the stack frame set up by an earlier ENTER instruction. The
LEAVE instruction copies the frame pointer (in the EBP register) into
the stack pointer register (ESP), which releases the stack space
allocated to the stack frame. The old frame pointer (the frame pointer
for the calling procedure that was saved by the ENTER instruction) is
then popped from the stack into the EBP register, restoring the calling
procedure’s stack frame."
To solve this, update Ebp/Rbp too when Esp/Rsp is updated
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Ge Song <ge.song@hxt-semitech.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-09-06 05:11:35 +02:00
|
|
|
JumpBuffer.Rbp = JumpBuffer.Rbp + DebugAgentContext.StackMigrateOffset;
|
2014-01-21 20:38:25 +01:00
|
|
|
#endif
|
|
|
|
LongJump (&JumpBuffer, (UINTN)-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
SaveAndSetDebugTimerInterrupt (OldStatus);
|
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|