edkII update in preparation of upgrade OC to 0.9.7.

This commit is contained in:
jief666 2023-12-31 13:43:34 +01:00
parent f6fcfeccbd
commit 3fd62f2756
1820 changed files with 316531 additions and 38757 deletions

View File

@ -28,9 +28,11 @@
FLASH_DEFINITION = Clover.fdf
!endif
!ifndef OPENSSL_VERSION
DEFINE OPENSSL_VERSION = 1.0.1e
!endif
!ifndef OPENSSL_VERSION
DEFINE OPENSSL_VERSION = 1.0.1e
!endif
!include MdePkg/MdeLibs.dsc.inc
################################################################################
#

View File

@ -1294,15 +1294,15 @@ AtaPassThruPassThru (
Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);
// DBG(L"This->Mode->IoAlign=%d Packet=%x Packet->InDataBuffer=%x\n", This->Mode->IoAlign, (UINTN)Packet, (UINTN)Packet->InDataBuffer);
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->InDataBuffer, This->Mode->IoAlign)) {
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED((UINTN)Packet->InDataBuffer, This->Mode->IoAlign)) {
return EFI_INVALID_PARAMETER;
}
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->OutDataBuffer, This->Mode->IoAlign)) {
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED((UINTN)Packet->OutDataBuffer, This->Mode->IoAlign)) {
return EFI_INVALID_PARAMETER;
}
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->Asb, This->Mode->IoAlign)) {
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED((UINTN)Packet->Asb, This->Mode->IoAlign)) {
return EFI_INVALID_PARAMETER;
}
@ -2011,17 +2011,17 @@ ExtScsiPassThruPassThru (
return EFI_INVALID_PARAMETER;
}
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->InDataBuffer, This->Mode->IoAlign)) {
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED((UINTN)Packet->InDataBuffer, This->Mode->IoAlign)) {
// DBG(L"IN not aligned\n");
return EFI_INVALID_PARAMETER;
}
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->OutDataBuffer, This->Mode->IoAlign)) {
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED((UINTN)Packet->OutDataBuffer, This->Mode->IoAlign)) {
// DBG(L"OUT not aligned\n");
return EFI_INVALID_PARAMETER;
}
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->SenseData, This->Mode->IoAlign)) {
if ((This->Mode->IoAlign > 1) && !IS_ALIGNED((UINTN)Packet->SenseData, This->Mode->IoAlign)) {
// DBG(L"SenseData not aligned\n");
return EFI_INVALID_PARAMETER;
}

View File

@ -149,7 +149,7 @@ struct _ATA_NONBLOCK_TASK {
//
#define ATA_ATAPI_TIMEOUT EFI_TIMER_PERIOD_SECONDS(3)
#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0)
//#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0)
#define ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS(a) \
CR (a, \

View File

@ -84,7 +84,7 @@
#define ATA_TASK_SIGNATURE SIGNATURE_32 ('A', 'T', 'S', 'K')
#define ATA_DEVICE_SIGNATURE SIGNATURE_32 ('A', 'B', 'I', 'D')
#define ATA_SUB_TASK_SIGNATURE SIGNATURE_32 ('A', 'S', 'T', 'S')
#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0)
//#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0)
#define ROUNDUP512(x) (((x) % 512 == 0) ? (x) : ((x) / 512 + 1) * 512)

View File

@ -1000,7 +1000,7 @@ TrustTransferAtaDevice (
// Check the alignment of the incoming buffer prior to invoking underlying ATA PassThru
//
AtaPassThru = AtaDevice->AtaBusDriverData->AtaPassThru;
if ((AtaPassThru->Mode->IoAlign > 1) && !IS_ALIGNED (Buffer, AtaPassThru->Mode->IoAlign)) {
if ((AtaPassThru->Mode->IoAlign > 1) && !IS_ALIGNED ((UINTN)Buffer, AtaPassThru->Mode->IoAlign)) {
NewBuffer = AllocateAlignedBuffer (AtaDevice, TransferLength);
if (NewBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;

16
Ext4Pkg/Ext4.dsc.inc Normal file
View File

@ -0,0 +1,16 @@
## @file
# Ext4 DSC include file for Platform DSC
#
# SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
!include Ext4Pkg/Ext4Defines.dsc.inc
[LibraryClasses]
!include Ext4Pkg/Ext4Libs.dsc.inc
[Components.common]
!include Ext4Pkg/Ext4Components.dsc.inc

11
Ext4Pkg/Ext4.fdf.inc Normal file
View File

@ -0,0 +1,11 @@
## @file
# Ext4 FDF include file for All Architectures.
#
# SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
!if $(EXT4_ENABLE) == TRUE
INF Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
!endif

View File

@ -0,0 +1,14 @@
## @file
# Ext4 DSC include file for [Components] section of all Architectures.
#
# SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
!if $(EXT4_ENABLE) == TRUE
Ext4Pkg/Ext4Dxe/Ext4Dxe.inf {
<PcdsFixedAtBuild>
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000007
}
!endif

View File

@ -0,0 +1,14 @@
## @file
# Ext4 DSC include file for [Defines] section of all Architectures.
#
# SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
!ifndef EXT4_ENABLE
#
# This flag is to enable or disable the ext4 feature.
#
DEFINE EXT4_ENABLE = TRUE
!endif

View File

@ -0,0 +1,229 @@
/** @file
Block group related routines
Copyright (c) 2021 Pedro Falcato All rights reserved.
Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
/**
Retrieves a block group descriptor of the ext4 filesystem.
@param[in] Partition Pointer to the opened ext4 partition.
@param[in] BlockGroup Block group number.
@return A pointer to the block group descriptor.
**/
EXT4_BLOCK_GROUP_DESC *
Ext4GetBlockGroupDesc (
IN EXT4_PARTITION *Partition,
IN UINT32 BlockGroup
)
{
// Maybe assert that the block group nr isn't a nonsense number?
return (EXT4_BLOCK_GROUP_DESC *)((CHAR8 *)Partition->BlockGroups + BlockGroup * Partition->DescSize);
}
/**
Reads an inode from disk.
@param[in] Partition Pointer to the opened partition.
@param[in] InodeNum Number of the desired Inode
@param[out] OutIno Pointer to where it will be stored a pointer to the read inode.
@return Status of the inode read.
**/
EFI_STATUS
Ext4ReadInode (
IN EXT4_PARTITION *Partition,
IN EXT4_INO_NR InodeNum,
OUT EXT4_INODE **OutIno
)
{
UINT64 InodeOffset;
UINT32 BlockGroupNumber;
EXT4_INODE *Inode;
EXT4_BLOCK_GROUP_DESC *BlockGroup;
EXT4_BLOCK_NR InodeTableStart;
EFI_STATUS Status;
if (!EXT4_IS_VALID_INODE_NR (Partition, InodeNum)) {
DEBUG ((DEBUG_ERROR, "[ext4] Error reading inode: inode number %lu isn't valid\n", InodeNum));
return EFI_VOLUME_CORRUPTED;
}
BlockGroupNumber = (UINT32)DivU64x64Remainder (
InodeNum - 1,
Partition->SuperBlock.s_inodes_per_group,
&InodeOffset
);
// Check for the block group number's correctness
if (BlockGroupNumber >= Partition->NumberBlockGroups) {
return EFI_VOLUME_CORRUPTED;
}
Inode = Ext4AllocateInode (Partition);
if (Inode == NULL) {
return EFI_OUT_OF_RESOURCES;
}
BlockGroup = Ext4GetBlockGroupDesc (Partition, BlockGroupNumber);
// Note: We'll need to check INODE_UNINIT and friends when/if we add write support
InodeTableStart = EXT4_BLOCK_NR_FROM_HALFS (
Partition,
BlockGroup->bg_inode_table_lo,
BlockGroup->bg_inode_table_hi
);
Status = Ext4ReadDiskIo (
Partition,
Inode,
Partition->InodeSize,
EXT4_BLOCK_TO_BYTES (Partition, InodeTableStart) + MultU64x32 (InodeOffset, Partition->InodeSize)
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"[ext4] Error reading inode: status %x; inode offset %lx"
" inode table start %lu block group %lu\n",
Status,
InodeOffset,
InodeTableStart,
BlockGroupNumber
));
FreePool (Inode);
return Status;
}
if (!Ext4CheckInodeChecksum (Partition, Inode, InodeNum)) {
DEBUG ((
DEBUG_ERROR,
"[ext4] Inode %llu has invalid checksum (calculated %x)\n",
InodeNum,
Ext4CalculateInodeChecksum (Partition, Inode, InodeNum)
));
FreePool (Inode);
return EFI_VOLUME_CORRUPTED;
}
*OutIno = Inode;
return EFI_SUCCESS;
}
/**
Calculates the checksum of the block group descriptor for METADATA_CSUM enabled filesystems.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] BlockGroupDesc Pointer to the block group descriptor.
@param[in] BlockGroupNum Number of the block group.
@return The checksum.
**/
STATIC
UINT16
Ext4CalculateBlockGroupDescChecksumMetadataCsum (
IN CONST EXT4_PARTITION *Partition,
IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
IN UINT32 BlockGroupNum
)
{
UINT32 Csum;
UINT16 Dummy;
Dummy = 0;
Csum = Ext4CalculateChecksum (Partition, &BlockGroupNum, sizeof (BlockGroupNum), Partition->InitialSeed);
Csum = Ext4CalculateChecksum (Partition, BlockGroupDesc, OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_checksum), Csum);
Csum = Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), Csum);
Csum =
Ext4CalculateChecksum (
Partition,
&BlockGroupDesc->bg_block_bitmap_hi,
Partition->DescSize - OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_block_bitmap_hi),
Csum
);
return (UINT16)Csum;
}
/**
Calculates the checksum of the block group descriptor for GDT_CSUM enabled filesystems.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] BlockGroupDesc Pointer to the block group descriptor.
@param[in] BlockGroupNum Number of the block group.
@return The checksum.
**/
STATIC
UINT16
Ext4CalculateBlockGroupDescChecksumGdtCsum (
IN CONST EXT4_PARTITION *Partition,
IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
IN UINT32 BlockGroupNum
)
{
UINT16 Csum;
Csum = CalculateCrc16Ansi (Partition->SuperBlock.s_uuid, sizeof (Partition->SuperBlock.s_uuid), 0xFFFF);
Csum = CalculateCrc16Ansi (&BlockGroupNum, sizeof (BlockGroupNum), Csum);
Csum = CalculateCrc16Ansi (BlockGroupDesc, OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_checksum), Csum);
Csum =
CalculateCrc16Ansi (
&BlockGroupDesc->bg_block_bitmap_hi,
Partition->DescSize - OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_block_bitmap_hi),
Csum
);
return Csum;
}
/**
Checks if the checksum of the block group descriptor is correct.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] BlockGroupDesc Pointer to the block group descriptor.
@param[in] BlockGroupNum Number of the block group.
@return TRUE if checksum is correct, FALSE if there is corruption.
**/
BOOLEAN
Ext4VerifyBlockGroupDescChecksum (
IN CONST EXT4_PARTITION *Partition,
IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
IN UINT32 BlockGroupNum
)
{
if (!EXT4_HAS_METADATA_CSUM (Partition) && !EXT4_HAS_GDT_CSUM (Partition)) {
return TRUE;
}
return Ext4CalculateBlockGroupDescChecksum (Partition, BlockGroupDesc, BlockGroupNum) == BlockGroupDesc->bg_checksum;
}
/**
Calculates the checksum of the block group descriptor.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] BlockGroupDesc Pointer to the block group descriptor.
@param[in] BlockGroupNum Number of the block group.
@return The checksum.
**/
UINT16
Ext4CalculateBlockGroupDescChecksum (
IN CONST EXT4_PARTITION *Partition,
IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
IN UINT32 BlockGroupNum
)
{
if (EXT4_HAS_METADATA_CSUM (Partition)) {
return Ext4CalculateBlockGroupDescChecksumMetadataCsum (Partition, BlockGroupDesc, BlockGroupNum);
} else if (EXT4_HAS_GDT_CSUM (Partition)) {
return Ext4CalculateBlockGroupDescChecksumGdtCsum (Partition, BlockGroupDesc, BlockGroupNum);
}
return 0;
}

285
Ext4Pkg/Ext4Dxe/BlockMap.c Normal file
View File

@ -0,0 +1,285 @@
/** @file
Implementation of routines that deal with ext2/3 block maps.
Copyright (c) 2022 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Ext4Dxe.h>
// Note: The largest path we can take uses up 4 indices
#define EXT4_MAX_BLOCK_PATH 4
typedef enum ext4_logical_block_type {
EXT4_TYPE_DIRECT_BLOCK = 0,
EXT4_TYPE_SINGLY_BLOCK,
EXT4_TYPE_DOUBLY_BLOCK,
EXT4_TYPE_TREBLY_BLOCK,
EXT4_TYPE_BAD_BLOCK
} EXT4_LOGICAL_BLOCK_TYPE;
/**
@brief Detect the type of path the logical block will follow
@param[in] LogicalBlock The logical block
@param[in] Partition Pointer to an EXT4_PARTITION
@return The type of path the logical block will need to follow
*/
STATIC
EXT4_LOGICAL_BLOCK_TYPE
Ext4DetectBlockType (
IN UINT32 LogicalBlock,
IN CONST EXT4_PARTITION *Partition
)
{
UINT32 Entries;
UINT32 MinSinglyBlock;
UINT32 MinDoublyBlock;
UINT32 MinTreblyBlock;
UINT32 MinQuadBlock;
Entries = (Partition->BlockSize / sizeof (UINT32));
MinSinglyBlock = EXT4_DBLOCKS;
MinDoublyBlock = Entries + MinSinglyBlock;
MinTreblyBlock = Entries * Entries + MinDoublyBlock;
MinQuadBlock = Entries * Entries * Entries + MinTreblyBlock; // Doesn't actually exist
if (LogicalBlock < MinSinglyBlock) {
return EXT4_TYPE_DIRECT_BLOCK;
} else if ((LogicalBlock >= MinSinglyBlock) && (LogicalBlock < MinDoublyBlock)) {
return EXT4_TYPE_SINGLY_BLOCK;
} else if ((LogicalBlock >= MinDoublyBlock) && (LogicalBlock < MinTreblyBlock)) {
return EXT4_TYPE_DOUBLY_BLOCK;
} else if (((LogicalBlock >= MinTreblyBlock) && (LogicalBlock < MinQuadBlock))) {
return EXT4_TYPE_TREBLY_BLOCK;
} else {
return EXT4_TYPE_BAD_BLOCK;
}
}
/**
@brief Get a block's path in indices
@param[in] Partition Pointer to an EXT4_PARTITION
@param[in] LogicalBlock Logical block
@param[out] BlockPath Pointer to an array of EXT4_MAX_BLOCK_PATH elements, where the
indices we'll need to read are inserted.
@return The number of path elements that are required (and were inserted in BlockPath)
*/
UINTN
Ext4GetBlockPath (
IN CONST EXT4_PARTITION *Partition,
IN UINT32 LogicalBlock,
OUT EXT2_BLOCK_NR BlockPath[EXT4_MAX_BLOCK_PATH]
)
{
// The logic behind the block map is very much like a page table
// Let's think of blocks with 512 entries (exactly like a page table on x64).
// On doubly indirect block paths, we subtract the min doubly blocks from the logical block.
// The top 9 bits of the result are the index inside the dind block, the bottom 9 bits are the
// index inside the ind block. Since Entries is always a power of 2, entries - 1 will give us
// a mask of the BlockMapBits.
// Note that all this math could be done with ands and shifts (similar implementations exist
// in a bunch of other places), but I'm doing it a simplified way with divs and modulus,
// since it's not going to be a bottleneck anyway.
UINT32 Entries;
UINT32 EntriesEntries;
UINT32 MinSinglyBlock;
UINT32 MinDoublyBlock;
UINT32 MinTreblyBlock;
EXT4_LOGICAL_BLOCK_TYPE Type;
Entries = (Partition->BlockSize / sizeof (UINT32));
EntriesEntries = Entries * Entries;
MinSinglyBlock = EXT4_DBLOCKS;
MinDoublyBlock = Entries + MinSinglyBlock;
MinTreblyBlock = EntriesEntries + MinDoublyBlock;
Type = Ext4DetectBlockType (LogicalBlock, Partition);
switch (Type) {
case EXT4_TYPE_DIRECT_BLOCK:
BlockPath[0] = LogicalBlock;
break;
case EXT4_TYPE_SINGLY_BLOCK:
BlockPath[0] = EXT4_IND_BLOCK;
BlockPath[1] = LogicalBlock - EXT4_DBLOCKS;
break;
case EXT4_TYPE_DOUBLY_BLOCK:
BlockPath[0] = EXT4_DIND_BLOCK;
LogicalBlock -= MinDoublyBlock;
BlockPath[1] = LogicalBlock / Entries;
BlockPath[2] = LogicalBlock % Entries;
break;
case EXT4_TYPE_TREBLY_BLOCK:
BlockPath[0] = EXT4_DIND_BLOCK;
LogicalBlock -= MinTreblyBlock;
BlockPath[1] = LogicalBlock / EntriesEntries;
BlockPath[2] = (LogicalBlock % EntriesEntries) / Entries;
BlockPath[3] = (LogicalBlock % EntriesEntries) % Entries;
break;
default:
// EXT4_TYPE_BAD_BLOCK
break;
}
return Type + 1;
}
/**
@brief Get an extent from a block map
Note: Also parses file holes and creates uninitialized extents from them.
@param[in] Buffer Buffer of block pointers
@param[in] IndEntries Number of entries in this block pointer table
@param[in] StartIndex The start index from which we want to find a contiguous extent
@param[out] Extent Pointer to the resulting EXT4_EXTENT
*/
VOID
Ext4GetExtentInBlockMap (
IN CONST UINT32 *Buffer,
IN CONST UINT32 IndEntries,
IN UINT32 StartIndex,
OUT EXT4_EXTENT *Extent
)
{
UINT32 Index;
UINT32 FirstBlock;
UINT32 LastBlock;
UINT16 Count;
Count = 1;
LastBlock = Buffer[StartIndex];
FirstBlock = LastBlock;
if (FirstBlock == EXT4_BLOCK_FILE_HOLE) {
// File hole, let's see how many blocks this hole spans
Extent->ee_start_hi = 0;
Extent->ee_start_lo = 0;
for (Index = StartIndex + 1; Index < IndEntries; Index++) {
if (Count == EXT4_EXTENT_MAX_INITIALIZED - 1) {
// We've reached the max size of an uninit extent, break
break;
}
if (Buffer[Index] == EXT4_BLOCK_FILE_HOLE) {
Count++;
} else {
break;
}
}
// We mark the extent as uninitialized, although there's a difference between uninit
// extents and file holes.
Extent->ee_len = EXT4_EXTENT_MAX_INITIALIZED + Count;
return;
}
for (Index = StartIndex + 1; Index < IndEntries; Index++) {
if (Count == EXT4_EXTENT_MAX_INITIALIZED) {
// We've reached the max size of an extent, break
break;
}
if ((Buffer[Index] == LastBlock + 1) && (Buffer[Index] != EXT4_BLOCK_FILE_HOLE)) {
Count++;
} else {
break;
}
LastBlock = Buffer[Index];
}
Extent->ee_start_lo = FirstBlock;
Extent->ee_start_hi = 0;
Extent->ee_len = Count;
}
/**
Retrieves an extent from an EXT2/3 inode (with a blockmap).
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] File Pointer to the opened file.
@param[in] LogicalBlock Block number which the returned extent must cover.
@param[out] Extent Pointer to the output buffer, where the extent will be copied to.
@retval EFI_SUCCESS Retrieval was successful.
@retval EFI_NO_MAPPING Block has no mapping.
**/
EFI_STATUS
Ext4GetBlocks (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
IN EXT2_BLOCK_NR LogicalBlock,
OUT EXT4_EXTENT *Extent
)
{
EXT4_INODE *Inode;
EXT2_BLOCK_NR BlockPath[EXT4_MAX_BLOCK_PATH];
UINTN BlockPathLength;
UINTN Index;
UINT32 *Buffer;
EFI_STATUS Status;
UINT32 Block;
UINT32 BlockIndex;
Inode = File->Inode;
BlockPathLength = Ext4GetBlockPath (Partition, LogicalBlock, BlockPath);
if (BlockPathLength - 1 == EXT4_TYPE_BAD_BLOCK) {
// Bad logical block (out of range)
return EFI_NO_MAPPING;
}
Extent->ee_block = LogicalBlock;
if (BlockPathLength == 1) {
// Fast path for blocks 0 - 12 that skips allocations
Ext4GetExtentInBlockMap (Inode->i_data, EXT4_DBLOCKS, BlockPath[0], Extent);
return EFI_SUCCESS;
}
Buffer = AllocatePool (Partition->BlockSize);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// Note the BlockPathLength - 1 so we don't end up reading the final block
for (Index = 0; Index < BlockPathLength - 1; Index++) {
BlockIndex = BlockPath[Index];
if (Index == 0) {
Block = Inode->i_data[BlockIndex];
} else {
Block = Buffer[BlockIndex];
}
if (Block == EXT4_BLOCK_FILE_HOLE) {
FreePool (Buffer);
return EFI_NO_MAPPING;
}
Status = Ext4ReadBlocks (Partition, Buffer, 1, Block);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
}
Ext4GetExtentInBlockMap (
Buffer,
Partition->BlockSize / sizeof (UINT32),
BlockPath[BlockPathLength - 1],
Extent
);
FreePool (Buffer);
return EFI_SUCCESS;
}

195
Ext4Pkg/Ext4Dxe/Collation.c Normal file
View File

@ -0,0 +1,195 @@
/** @file
Unicode collation routines
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/UnicodeCollation.h>
STATIC EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollationInterface = NULL;
/*
* Note: This code is heavily based on FatPkg's Unicode collation, since they seem to know what
* they're doing.
* PS: Maybe all this code could be put in a library? It looks heavily shareable.
**/
/**
Check if unicode collation is initialized
@retval TRUE if Ext4InitialiseUnicodeCollation() was already called successfully
@retval FALSE if Ext4InitialiseUnicodeCollation() was not yet called successfully
**/
STATIC
BOOLEAN
Ext4IsCollationInitialized (
VOID
)
{
return gUnicodeCollationInterface != NULL;
}
/**
Worker function to initialize Unicode Collation support.
It tries to locate Unicode Collation (2) protocol and matches it with current
platform language code.
@param[in] DriverHandle The handle used to open Unicode Collation (2) protocol.
@param[in] ProtocolGuid The pointer to Unicode Collation (2) protocol GUID.
@param[in] VariableName The name of the RFC 4646 or ISO 639-2 language variable.
@param[in] DefaultLanguage The default language in case the RFC 4646 or ISO 639-2 language is absent.
@retval EFI_SUCCESS The Unicode Collation (2) protocol has been successfully located.
@retval Others The Unicode Collation (2) protocol has not been located.
**/
STATIC
EFI_STATUS
Ext4InitialiseUnicodeCollationInternal (
IN EFI_HANDLE DriverHandle,
IN EFI_GUID *ProtocolGuid,
IN CONST CHAR16 *VariableName,
IN CONST CHAR8 *DefaultLanguage
)
{
UINTN NumHandles;
EFI_HANDLE *Handles;
EFI_UNICODE_COLLATION_PROTOCOL *Uci;
BOOLEAN Iso639Language;
CHAR8 *Language;
EFI_STATUS RetStatus;
EFI_STATUS Status;
UINTN Idx;
CHAR8 *BestLanguage;
Iso639Language = (BOOLEAN)(ProtocolGuid == &gEfiUnicodeCollationProtocolGuid);
RetStatus = EFI_UNSUPPORTED;
GetEfiGlobalVariable2 (VariableName, (VOID **)&Language, NULL);
Status = gBS->LocateHandleBuffer (
ByProtocol,
ProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}
for (Idx = 0; Idx < NumHandles; Idx++) {
Status = gBS->OpenProtocol (
Handles[Idx],
ProtocolGuid,
(VOID **)&Uci,
DriverHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
continue;
}
BestLanguage = GetBestLanguage (
Uci->SupportedLanguages,
Iso639Language,
(Language == NULL) ? "" : Language,
DefaultLanguage,
NULL
);
if (BestLanguage != NULL) {
FreePool (BestLanguage);
gUnicodeCollationInterface = Uci;
RetStatus = EFI_SUCCESS;
break;
}
}
if (Language != NULL) {
FreePool (Language);
}
FreePool (Handles);
return RetStatus;
}
/**
Initialises Unicode collation, which is needed for case-insensitive string comparisons
within the driver (a good example of an application of this is filename comparison).
@param[in] DriverHandle Handle to the driver image.
@retval EFI_SUCCESS Unicode collation was successfully initialised.
@retval !EFI_SUCCESS Failure.
**/
EFI_STATUS
Ext4InitialiseUnicodeCollation (
EFI_HANDLE DriverHandle
)
{
EFI_STATUS Status;
Status = EFI_UNSUPPORTED;
// If already done, just return success.
if (Ext4IsCollationInitialized ()) {
return EFI_SUCCESS;
}
//
// First try to use RFC 4646 Unicode Collation 2 Protocol.
//
Status = Ext4InitialiseUnicodeCollationInternal (
DriverHandle,
&gEfiUnicodeCollation2ProtocolGuid,
L"PlatformLang",
(CONST CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultPlatformLang)
);
//
// If the attempt to use Unicode Collation 2 Protocol fails, then we fall back
// on the ISO 639-2 Unicode Collation Protocol.
//
if (EFI_ERROR (Status)) {
Status = Ext4InitialiseUnicodeCollationInternal (
DriverHandle,
&gEfiUnicodeCollationProtocolGuid,
L"Lang",
(CONST CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultLang)
);
}
return Status;
}
/**
Does a case-insensitive string comparison. Refer to EFI_UNICODE_COLLATION_PROTOCOL's StriColl
for more details.
@param[in] Str1 Pointer to a null terminated string.
@param[in] Str2 Pointer to a null terminated string.
@retval 0 Str1 is equivalent to Str2.
@retval >0 Str1 is lexically greater than Str2.
@retval <0 Str1 is lexically less than Str2.
**/
INTN
Ext4StrCmpInsensitive (
IN CHAR16 *Str1,
IN CHAR16 *Str2
)
{
ASSERT (gUnicodeCollationInterface != NULL);
return gUnicodeCollationInterface->StriColl (gUnicodeCollationInterface, Str1, Str2);
}

708
Ext4Pkg/Ext4Dxe/Directory.c Normal file
View File

@ -0,0 +1,708 @@
/** @file
Directory related routines
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
#include <Library/BaseUcs2Utf8Lib.h>
/**
Retrieves the filename of the directory entry and converts it to UTF-16/UCS-2
@param[in] Entry Pointer to a EXT4_DIR_ENTRY.
@param[out] Ucs2FileName Pointer to an array of CHAR16's, of size EXT4_NAME_MAX + 1.
@retval EFI_SUCCESS The filename was successfully retrieved and converted to UCS2.
@retval EFI_INVALID_PARAMETER The filename is not valid UTF-8.
@retval !EFI_SUCCESS Failure.
**/
EFI_STATUS
Ext4GetUcs2DirentName (
IN EXT4_DIR_ENTRY *Entry,
OUT CHAR16 Ucs2FileName[EXT4_NAME_MAX + 1]
)
{
CHAR8 Utf8NameBuf[EXT4_NAME_MAX + 1];
UINT16 *Str;
UINT8 Index;
EFI_STATUS Status;
for (Index = 0; Index < Entry->name_len; ++Index) {
if (Entry->name[Index] == '\0') {
return EFI_INVALID_PARAMETER;
}
Utf8NameBuf[Index] = Entry->name[Index];
}
Utf8NameBuf[Entry->name_len] = '\0';
// Unfortunately, BaseUcs2Utf8Lib doesn't have a convert-buffer-to-buffer-like
// function. Therefore, we need to allocate from the pool (inside UTF8StrToUCS2),
// copy it to our out buffer (Ucs2FileName) and free.
Status = UTF8StrToUCS2 (Utf8NameBuf, &Str);
if (EFI_ERROR (Status)) {
return Status;
}
Status = StrCpyS (Ucs2FileName, EXT4_NAME_MAX + 1, Str);
FreePool (Str);
return Status;
}
/**
Validates a directory entry.
@param[in] Dirent Pointer to the directory entry.
@retval TRUE Valid directory entry.
FALSE Invalid directory entry.
**/
STATIC
BOOLEAN
Ext4ValidDirent (
IN CONST EXT4_DIR_ENTRY *Dirent
)
{
UINTN RequiredSize;
RequiredSize = Dirent->name_len + EXT4_MIN_DIR_ENTRY_LEN;
if (Dirent->rec_len < RequiredSize) {
DEBUG ((DEBUG_ERROR, "[ext4] dirent size %lu too small (compared to %lu)\n", Dirent->rec_len, RequiredSize));
return FALSE;
}
// Dirent sizes need to be 4 byte aligned
if ((Dirent->rec_len % 4) != 0) {
return FALSE;
}
return TRUE;
}
/**
Retrieves a directory entry.
@param[in] Directory Pointer to the opened directory.
@param[in] NameUnicode Pointer to the UCS-2 formatted filename.
@param[in] Partition Pointer to the ext4 partition.
@param[out] Result Pointer to the destination directory entry.
@return The result of the operation.
**/
EFI_STATUS
Ext4RetrieveDirent (
IN EXT4_FILE *Directory,
IN CONST CHAR16 *Name,
IN EXT4_PARTITION *Partition,
OUT EXT4_DIR_ENTRY *Result
)
{
EFI_STATUS Status;
CHAR8 *Buf;
UINT64 Off;
EXT4_INODE *Inode;
UINT64 DirInoSize;
UINT32 BlockRemainder;
UINTN Length;
EXT4_DIR_ENTRY *Entry;
UINTN RemainingBlock;
CHAR16 DirentUcs2Name[EXT4_NAME_MAX + 1];
UINTN ToCopy;
UINTN BlockOffset;
Buf = AllocatePool (Partition->BlockSize);
if (Buf == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Off = 0;
Inode = Directory->Inode;
DirInoSize = EXT4_INODE_SIZE (Inode);
DivU64x32Remainder (DirInoSize, Partition->BlockSize, &BlockRemainder);
if (BlockRemainder != 0) {
// Directory inodes need to have block aligned sizes
Status = EFI_VOLUME_CORRUPTED;
goto Out;
}
while (Off < DirInoSize) {
Length = Partition->BlockSize;
Status = Ext4Read (Partition, Directory, Buf, Off, &Length);
if (Status != EFI_SUCCESS) {
goto Out;
}
for (BlockOffset = 0; BlockOffset < Partition->BlockSize; ) {
Entry = (EXT4_DIR_ENTRY *)(Buf + BlockOffset);
RemainingBlock = Partition->BlockSize - BlockOffset;
// Check if the minimum directory entry fits inside [BlockOffset, EndOfBlock]
if (RemainingBlock < EXT4_MIN_DIR_ENTRY_LEN) {
Status = EFI_VOLUME_CORRUPTED;
goto Out;
}
if (!Ext4ValidDirent (Entry)) {
Status = EFI_VOLUME_CORRUPTED;
goto Out;
}
if ((Entry->name_len > RemainingBlock) || (Entry->rec_len > RemainingBlock)) {
// Corrupted filesystem
Status = EFI_VOLUME_CORRUPTED;
goto Out;
}
// Unused entry
if (Entry->inode == 0) {
BlockOffset += Entry->rec_len;
continue;
}
Status = Ext4GetUcs2DirentName (Entry, DirentUcs2Name);
/* In theory, this should never fail.
* In reality, it's quite possible that it can fail, considering filenames in
* Linux (and probably other nixes) are just null-terminated bags of bytes, and don't
* need to form valid ASCII/UTF-8 sequences.
*/
if (EFI_ERROR (Status)) {
if (Status == EFI_INVALID_PARAMETER) {
// If we error out due to a bad UTF-8 sequence (see Ext4GetUcs2DirentName), skip this entry.
// I'm not sure if this is correct behaviour, but I don't think there's a precedent here.
BlockOffset += Entry->rec_len;
continue;
}
// Other sorts of errors should just error out.
FreePool (Buf);
return Status;
}
if ((Entry->name_len == StrLen (Name)) &&
!Ext4StrCmpInsensitive (DirentUcs2Name, (CHAR16 *)Name))
{
ToCopy = MIN (Entry->rec_len, sizeof (EXT4_DIR_ENTRY));
CopyMem (Result, Entry, ToCopy);
Status = EFI_SUCCESS;
goto Out;
}
BlockOffset += Entry->rec_len;
}
Off += Partition->BlockSize;
}
Status = EFI_NOT_FOUND;
Out:
FreePool (Buf);
return Status;
}
/**
Opens a file using a directory entry.
@param[in] Partition Pointer to the ext4 partition.
@param[in] OpenMode Mode in which the file is supposed to be open.
@param[out] OutFile Pointer to the newly opened file.
@param[in] Entry Directory entry to be used.
@param[in] Directory Pointer to the opened directory.
@retval EFI_STATUS Result of the operation
**/
EFI_STATUS
Ext4OpenDirent (
IN EXT4_PARTITION *Partition,
IN UINT64 OpenMode,
OUT EXT4_FILE **OutFile,
IN EXT4_DIR_ENTRY *Entry,
IN EXT4_FILE *Directory
)
{
EFI_STATUS Status;
CHAR16 FileName[EXT4_NAME_MAX + 1];
EXT4_FILE *File;
File = AllocateZeroPool (sizeof (EXT4_FILE));
if (File == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
Status = Ext4GetUcs2DirentName (Entry, FileName);
if (EFI_ERROR (Status)) {
goto Error;
}
if (StrCmp (FileName, L".") == 0) {
// We're using the parent directory's dentry
File->Dentry = Directory->Dentry;
ASSERT (File->Dentry != NULL);
Ext4RefDentry (File->Dentry);
} else if (StrCmp (FileName, L"..") == 0) {
// Using the parent's parent's dentry
File->Dentry = Directory->Dentry->Parent;
if (!File->Dentry) {
// Someone tried .. on root, so direct them to /
// This is an illegal EFI Open() but is possible to hit from a variety of internal code
File->Dentry = Directory->Dentry;
}
Ext4RefDentry (File->Dentry);
} else {
File->Dentry = Ext4CreateDentry (FileName, Directory->Dentry);
if (File->Dentry == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
}
Status = Ext4InitExtentsMap (File);
if (EFI_ERROR (Status)) {
goto Error;
}
File->InodeNum = Entry->inode;
Ext4SetupFile (File, Partition);
Status = Ext4ReadInode (Partition, Entry->inode, &File->Inode);
if (EFI_ERROR (Status)) {
goto Error;
}
*OutFile = File;
InsertTailList (&Partition->OpenFiles, &File->OpenFilesListNode);
return EFI_SUCCESS;
Error:
if (File != NULL) {
if (File->Dentry != NULL) {
Ext4UnrefDentry (File->Dentry);
}
if (File->ExtentsMap != NULL) {
OrderedCollectionUninit (File->ExtentsMap);
}
FreePool (File);
}
return Status;
}
/**
Opens a file.
@param[in] Directory Pointer to the opened directory.
@param[in] Name Pointer to the UCS-2 formatted filename.
@param[in] Partition Pointer to the ext4 partition.
@param[in] OpenMode Mode in which the file is supposed to be open.
@param[out] OutFile Pointer to the newly opened file.
@return Result of the operation.
**/
EFI_STATUS
Ext4OpenFile (
IN EXT4_FILE *Directory,
IN CONST CHAR16 *Name,
IN EXT4_PARTITION *Partition,
IN UINT64 OpenMode,
OUT EXT4_FILE **OutFile
)
{
EXT4_DIR_ENTRY Entry;
EFI_STATUS Status;
Status = Ext4RetrieveDirent (Directory, Name, Partition, &Entry);
if (EFI_ERROR (Status)) {
return Status;
}
// EFI requires us to error out on ".." opens for the root directory
if (Entry.inode == Directory->InodeNum) {
return EFI_NOT_FOUND;
}
return Ext4OpenDirent (Partition, OpenMode, OutFile, &Entry, Directory);
}
/**
Open the root directory on a volume.
@param[in] This A pointer to the volume to open the root directory.
@param[out] Root A pointer to the location to return the opened file handle for the
root directory.
@retval EFI_SUCCESS The device was opened.
@retval EFI_UNSUPPORTED This volume does not support the requested file system type.
@retval EFI_NO_MEDIA The device has no medium.
@retval EFI_DEVICE_ERROR The device reported an error.
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
@retval EFI_ACCESS_DENIED The service denied access to the file.
@retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources.
@retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
longer supported. Any existing file handles for this volume are
no longer valid. To access the files on the new medium, the
volume must be reopened with OpenVolume().
**/
EFI_STATUS
EFIAPI
Ext4OpenVolume (
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
OUT EFI_FILE_PROTOCOL **Root
)
{
EXT4_INODE *RootInode;
EFI_STATUS Status;
EXT4_FILE *RootDir;
EXT4_PARTITION *Partition;
Partition = (EXT4_PARTITION *)This;
Status = Ext4ReadInode (Partition, EXT4_ROOT_INODE_NR, &RootInode);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "[ext4] Could not open root inode - error %r\n", Status));
return Status;
}
RootDir = AllocateZeroPool (sizeof (EXT4_FILE));
if (RootDir == NULL) {
FreePool (RootInode);
return EFI_OUT_OF_RESOURCES;
}
RootDir->Inode = RootInode;
RootDir->InodeNum = EXT4_ROOT_INODE_NR;
Status = Ext4InitExtentsMap (RootDir);
if (EFI_ERROR (Status)) {
FreePool (RootInode);
FreePool (RootDir);
return EFI_OUT_OF_RESOURCES;
}
Ext4SetupFile (RootDir, Partition);
*Root = &RootDir->Protocol;
InsertTailList (&Partition->OpenFiles, &RootDir->OpenFilesListNode);
ASSERT (Partition->RootDentry != NULL);
RootDir->Dentry = Partition->RootDentry;
Ext4RefDentry (RootDir->Dentry);
return EFI_SUCCESS;
}
/**
Reads a directory entry.
@param[in] Partition Pointer to the ext4 partition.
@param[in] File Pointer to the open directory.
@param[out] Buffer Pointer to the output buffer.
@param[in] Offset Initial directory position.
@param[in out] OutLength Pointer to a UINTN that contains the length of the buffer,
and the length of the actual EFI_FILE_INFO after the call.
@return Result of the operation.
**/
EFI_STATUS
Ext4ReadDir (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT VOID *Buffer,
IN UINT64 Offset,
IN OUT UINTN *OutLength
)
{
EXT4_INODE *DirIno;
EFI_STATUS Status;
UINT64 DirInoSize;
UINTN Len;
UINT32 BlockRemainder;
EXT4_DIR_ENTRY Entry;
EXT4_FILE *TempFile;
BOOLEAN ShouldSkip;
BOOLEAN IsDotOrDotDot;
CHAR16 DirentUcs2Name[EXT4_NAME_MAX + 1];
DirIno = File->Inode;
Status = EFI_SUCCESS;
DirInoSize = EXT4_INODE_SIZE (DirIno);
DivU64x32Remainder (DirInoSize, Partition->BlockSize, &BlockRemainder);
if (BlockRemainder != 0) {
// Directory inodes need to have block aligned sizes
return EFI_VOLUME_CORRUPTED;
}
while (TRUE) {
TempFile = NULL;
// We (try to) read the maximum size of a directory entry at a time
// Note that we don't need to read any padding that may exist after it.
Len = sizeof (Entry);
Status = Ext4Read (Partition, File, &Entry, Offset, &Len);
if (EFI_ERROR (Status)) {
goto Out;
}
if (Len == 0) {
*OutLength = 0;
Status = EFI_SUCCESS;
goto Out;
}
if (Len < EXT4_MIN_DIR_ENTRY_LEN) {
Status = EFI_VOLUME_CORRUPTED;
goto Out;
}
// Invalid directory entry length
if (!Ext4ValidDirent (&Entry)) {
DEBUG ((DEBUG_ERROR, "[ext4] Invalid dirent at offset %lu\n", Offset));
Status = EFI_VOLUME_CORRUPTED;
goto Out;
}
// Check if the entire dir entry length fits in Len
if (Len < (UINTN)(EXT4_MIN_DIR_ENTRY_LEN + Entry.name_len)) {
Status = EFI_VOLUME_CORRUPTED;
goto Out;
}
// We don't care about passing . or .. entries to the caller of ReadDir(),
// since they're generally useless entries *and* may break things if too
// many callers assume FAT32.
// Entry.name_len may be 0 if it's a nameless entry, like an unused entry
// or a checksum at the end of the directory block.
// memcmp (and CompareMem) return 0 when the passed length is 0.
// We must bound name_len as > 0 and <= 2 to avoid any out-of-bounds accesses or bad detection of
// "." and "..".
IsDotOrDotDot = Entry.name_len > 0 && Entry.name_len <= 2 &&
CompareMem (Entry.name, "..", Entry.name_len) == 0;
// When inode = 0, it's unused. When name_len == 0, it's a nameless entry
// (which we should not expose to ReadDir).
ShouldSkip = Entry.inode == 0 || Entry.name_len == 0 || IsDotOrDotDot;
if (ShouldSkip) {
Offset += Entry.rec_len;
continue;
}
// Test if the dirent is valid utf-8. This is already done inside Ext4OpenDirent but EFI_INVALID_PARAMETER
// has the danger of its meaning being overloaded in many places, so we can't skip according to that.
// So test outside of it, explicitly.
Status = Ext4GetUcs2DirentName (&Entry, DirentUcs2Name);
if (EFI_ERROR (Status)) {
if (Status == EFI_INVALID_PARAMETER) {
// Bad UTF-8, skip.
Offset += Entry.rec_len;
continue;
}
goto Out;
}
Status = Ext4OpenDirent (Partition, EFI_FILE_MODE_READ, &TempFile, &Entry, File);
if (EFI_ERROR (Status)) {
goto Out;
}
Status = Ext4GetFileInfo (TempFile, Buffer, OutLength);
if (!EFI_ERROR (Status)) {
File->Position = Offset + Entry.rec_len;
}
Ext4CloseInternal (TempFile);
goto Out;
}
Status = EFI_SUCCESS;
Out:
return Status;
}
/**
Removes a dentry from the other's list.
@param[in out] Parent Pointer to the parent EXT4_DENTRY.
@param[in out] ToBeRemoved Pointer to the child EXT4_DENTRY.
**/
STATIC
VOID
Ext4RemoveDentry (
IN OUT EXT4_DENTRY *Parent,
IN OUT EXT4_DENTRY *ToBeRemoved
)
{
ASSERT (IsNodeInList (&ToBeRemoved->ListNode, &Parent->Children));
RemoveEntryList (&ToBeRemoved->ListNode);
}
/**
Adds a dentry to the other's list.
The dentry that is added to the other one's list gets ->Parent set to Parent,
and the parent gets its reference count incremented.
@param[in out] Parent Pointer to the parent EXT4_DENTRY.
@param[in out] ToBeAdded Pointer to the child EXT4_DENTRY.
**/
STATIC
VOID
Ext4AddDentry (
IN OUT EXT4_DENTRY *Parent,
IN OUT EXT4_DENTRY *ToBeAdded
)
{
ToBeAdded->Parent = Parent;
InsertTailList (&Parent->Children, &ToBeAdded->ListNode);
Ext4RefDentry (Parent);
}
/**
Creates a new dentry object.
@param[in] Name Name of the dentry.
@param[in out opt] Parent Parent dentry, if it's not NULL.
@return The new allocated and initialised dentry.
The ref count will be set to 1.
**/
EXT4_DENTRY *
Ext4CreateDentry (
IN CONST CHAR16 *Name,
IN OUT EXT4_DENTRY *Parent OPTIONAL
)
{
EXT4_DENTRY *Dentry;
EFI_STATUS Status;
Dentry = AllocateZeroPool (sizeof (EXT4_DENTRY));
if (Dentry == NULL) {
return NULL;
}
Dentry->RefCount = 1;
// This StrCpyS should not fail.
Status = StrCpyS (Dentry->Name, ARRAY_SIZE (Dentry->Name), Name);
ASSERT_EFI_ERROR (Status);
InitializeListHead (&Dentry->Children);
if (Parent != NULL) {
Ext4AddDentry (Parent, Dentry);
}
DEBUG ((DEBUG_FS, "[ext4] Created dentry %s\n", Name));
return Dentry;
}
/**
Increments the ref count of the dentry.
@param[in out] Dentry Pointer to a valid EXT4_DENTRY.
**/
VOID
Ext4RefDentry (
IN OUT EXT4_DENTRY *Dentry
)
{
UINTN OldRef;
OldRef = Dentry->RefCount;
Dentry->RefCount++;
// I'm not sure if this (Refcount overflow) is a valid concern,
// but it's better to be safe than sorry.
ASSERT (OldRef < Dentry->RefCount);
}
/**
Deletes the dentry.
@param[in out] Dentry Pointer to a valid EXT4_DENTRY.
**/
STATIC
VOID
Ext4DeleteDentry (
IN OUT EXT4_DENTRY *Dentry
)
{
if (Dentry->Parent) {
Ext4RemoveDentry (Dentry->Parent, Dentry);
Ext4UnrefDentry (Dentry->Parent);
}
DEBUG ((DEBUG_FS, "[ext4] Deleted dentry %s\n", Dentry->Name));
FreePool (Dentry);
}
/**
Decrements the ref count of the dentry.
If the ref count is 0, it's destroyed.
@param[in out] Dentry Pointer to a valid EXT4_DENTRY.
@retval True if it was destroyed, false if it's alive.
**/
BOOLEAN
Ext4UnrefDentry (
IN OUT EXT4_DENTRY *Dentry
)
{
Dentry->RefCount--;
if (Dentry->RefCount == 0) {
Ext4DeleteDentry (Dentry);
return TRUE;
}
return FALSE;
}

123
Ext4Pkg/Ext4Dxe/DiskUtil.c Normal file
View File

@ -0,0 +1,123 @@
/** @file
Disk utilities
Copyright (c) 2021 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
/**
Reads from the partition's disk using the DISK_IO protocol.
@param[in] Partition Pointer to the opened ext4 partition.
@param[out] Buffer Pointer to a destination buffer.
@param[in] Length Length of the destination buffer.
@param[in] Offset Offset, in bytes, of the location to read.
@return Success status of the disk read.
**/
EFI_STATUS
Ext4ReadDiskIo (
IN EXT4_PARTITION *Partition,
OUT VOID *Buffer,
IN UINTN Length,
IN UINT64 Offset
)
{
return EXT4_DISK_IO (Partition)->ReadDisk (
EXT4_DISK_IO (Partition),
EXT4_MEDIA_ID (Partition),
Offset,
Length,
Buffer
);
}
/**
Reads blocks from the partition's disk using the DISK_IO protocol.
@param[in] Partition Pointer to the opened ext4 partition.
@param[out] Buffer Pointer to a destination buffer.
@param[in] NumberBlocks Length of the read, in filesystem blocks.
@param[in] BlockNumber Starting block number.
@return Success status of the read.
**/
EFI_STATUS
Ext4ReadBlocks (
IN EXT4_PARTITION *Partition,
OUT VOID *Buffer,
IN UINTN NumberBlocks,
IN EXT4_BLOCK_NR BlockNumber
)
{
UINT64 Offset;
UINTN Length;
ASSERT (NumberBlocks != 0);
ASSERT (BlockNumber != EXT4_BLOCK_FILE_HOLE);
Offset = MultU64x32 (BlockNumber, Partition->BlockSize);
Length = NumberBlocks * Partition->BlockSize;
// Check for overflow on the block -> byte conversions.
// Partition->BlockSize is never 0, so we don't need to check for that.
if (DivU64x64Remainder (Offset, BlockNumber, NULL) != Partition->BlockSize) {
return EFI_INVALID_PARAMETER;
}
if (Length / NumberBlocks != Partition->BlockSize) {
return EFI_INVALID_PARAMETER;
}
return Ext4ReadDiskIo (Partition, Buffer, Length, Offset);
}
/**
Allocates a buffer and reads blocks from the partition's disk using the DISK_IO protocol.
This function is deprecated and will be removed in the future.
@param[in] Partition Pointer to the opened ext4 partition.
@param[in] NumberBlocks Length of the read, in filesystem blocks.
@param[in] BlockNumber Starting block number.
@return Buffer allocated by AllocatePool, or NULL if some part of the process
failed.
**/
VOID *
Ext4AllocAndReadBlocks (
IN EXT4_PARTITION *Partition,
IN UINTN NumberBlocks,
IN EXT4_BLOCK_NR BlockNumber
)
{
VOID *Buf;
UINTN Length;
// Check that number of blocks isn't empty, because
// this is incorrect condition for opened partition,
// so we just early-exit
if ((NumberBlocks == 0) || (BlockNumber == EXT4_BLOCK_FILE_HOLE)) {
return NULL;
}
Length = NumberBlocks * Partition->BlockSize;
// Check for integer overflow
if (Length / NumberBlocks != Partition->BlockSize) {
return NULL;
}
Buf = AllocatePool (Length);
if (Buf == NULL) {
return NULL;
}
if (Ext4ReadBlocks (Partition, Buf, NumberBlocks, BlockNumber) != EFI_SUCCESS) {
FreePool (Buf);
return NULL;
}
return Buf;
}

501
Ext4Pkg/Ext4Dxe/Ext4Disk.h Normal file
View File

@ -0,0 +1,501 @@
/** @file
Raw filesystem data structures
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
Layout of an EXT2/3/4 filesystem:
(note: this driver has been developed using
https://www.kernel.org/doc/html/latest/filesystems/ext4/index.html as
documentation).
An ext2/3/4 filesystem (here on out referred to as simply an ext4 filesystem,
due to the similarities) is composed of various concepts:
1) Superblock
The superblock is the structure near (1024 bytes offset from the start)
the start of the partition, and describes the filesystem in general.
Here, we get to know the size of the filesystem's blocks, which features
it supports or not, whether it's been cleanly unmounted, how many blocks
we have, etc.
2) Block groups
EXT4 filesystems are divided into block groups, and each block group covers
s_blocks_per_group(8 * Block Size) blocks. Each block group has an
associated block group descriptor; these are present directly after the
superblock. Each block group descriptor contains the location of the
inode table, and the inode and block bitmaps (note these bitmaps are only
a block long, which gets us the 8 * Block Size formula covered previously).
3) Blocks
The ext4 filesystem is divided in blocks, of size s_log_block_size ^ 1024.
Blocks can be allocated using individual block groups's bitmaps. Note
that block 0 is invalid and its presence on extents/block tables means
it's part of a file hole, and that particular location must be read as
a block full of zeros.
4) Inodes
The ext4 filesystem divides files/directories into inodes (originally
index nodes). Each file/socket/symlink/directory/etc (here on out referred
to as a file, since there is no distinction under the ext4 filesystem) is
stored as a /nameless/ inode, that is stored in some block group's inode
table. Each inode has s_inode_size size (or GOOD_OLD_INODE_SIZE if it's
an old filesystem), and holds various metadata about the file. Since the
largest inode structure right now is ~160 bytes, the rest of the inode
contains inline extended attributes. Inodes' data is stored using either
data blocks (under ext2/3) or extents (under ext4).
5) Extents
Ext4 inodes store data in extents. These let N contiguous logical blocks
that are represented by N contiguous physical blocks be represented by a
single extent structure, which minimizes filesystem metadata bloat and
speeds up block mapping (particularly due to the fact that high-quality
ext4 implementations like linux's try /really/ hard to make the file
contiguous, so it's common to have files with almost 0 fragmentation).
Inodes that use extents store them in a tree, and the top of the tree
is stored on i_data. The tree's leaves always start with an
EXT4_EXTENT_HEADER and contain EXT4_EXTENT_INDEX on eh_depth != 0 and
EXT4_EXTENT on eh_depth = 0; these entries are always sorted by logical
block.
6) Directories
Ext4 directories are files that store name -> inode mappings for the
logical directory; this is where files get their names, which means ext4
inodes do not themselves have names, since they can be linked (present)
multiple times with different names. Directories can store entries in two
different ways:
1) Classical linear directories: They store entries as a mostly-linked
mostly-list of EXT4_DIR_ENTRY.
2) Hash tree directories: These are used for larger directories, with
hundreds of entries, and are designed in a backwards compatible way.
These are not yet implemented in the Ext4Dxe driver.
7) Journal
Ext3/4 filesystems have a journal to help protect the filesystem against
system crashes. This is not yet implemented in Ext4Dxe but is described
in detail in the Linux kernel's documentation.
**/
#ifndef EXT4_DISK_H_
#define EXT4_DISK_H_
#include <Uefi.h>
#define EXT4_SUPERBLOCK_OFFSET 1024U
#define EXT4_SIGNATURE 0xEF53U
#define EXT4_FS_STATE_UNMOUNTED 0x1
#define EXT4_FS_STATE_ERRORS_DETECTED 0x2
#define EXT4_FS_STATE_RECOVERING_ORPHANS 0x4
#define EXT4_ERRORS_CONTINUE 1
#define EXT4_ERRORS_RO 2
#define EXT4_ERRORS_PANIC 3
#define EXT4_LINUX_ID 0
#define EXT4_GNU_HURD_ID 1
#define EXT4_MASIX_ID 2
#define EXT4_FREEBSD_ID 3
#define EXT4_LITES_ID 4
#define EXT4_GOOD_OLD_REV 0
#define EXT4_DYNAMIC_REV 1
#define EXT4_CHECKSUM_CRC32C 0x1
#define EXT4_FEATURE_COMPAT_DIR_PREALLOC 0x01
#define EXT4_FEATURE_COMPAT_IMAGIC_INODES 0x02
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x04
#define EXT4_FEATURE_COMPAT_EXT_ATTR 0x08
#define EXT4_FEATURE_COMPAT_RESIZE_INO 0x10
#define EXT4_FEATURE_COMPAT_DIR_INDEX 0x20
#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x00001
#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x00002
#define EXT4_FEATURE_INCOMPAT_RECOVER 0x00004
#define EXT4_FEATURE_INCOMPAT_JOURNAL_DEV 0x00008
#define EXT4_FEATURE_INCOMPAT_META_BG 0x00010
#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x00040
#define EXT4_FEATURE_INCOMPAT_64BIT 0x00080
#define EXT4_FEATURE_INCOMPAT_MMP 0x00100
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x00200
#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x00400
// It's not clear whether or not this feature (below) is used right now
#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x01000
#define EXT4_FEATURE_INCOMPAT_CSUM_SEED 0x02000
#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x04000
#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x08000
#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000
#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004// Unused
#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080// Not implemented in ext4
#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100
#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200
#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
#define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800// Not used
// We explicitly don't recognise this, so we get read only.
#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000
#define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000
/* Important notes about the features
* Absolutely needed features:
* 1) Every incompat, because we might want to mount root filesystems
* 2) Relevant RO_COMPATs(I'm not sure of what to do wrt quota, project)
**/
#define EXT4_INO_TYPE_FIFO 0x1000
#define EXT4_INO_TYPE_CHARDEV 0x2000
#define EXT4_INO_TYPE_DIR 0x4000
#define EXT4_INO_TYPE_BLOCKDEV 0x6000
#define EXT4_INO_TYPE_REGFILE 0x8000
#define EXT4_INO_TYPE_SYMLINK 0xA000
#define EXT4_INO_TYPE_UNIX_SOCK 0xC000
/* Inode flags */
#define EXT4_SECRM_FL 0x00000001
#define EXT4_UNRM_FL 0x00000002
#define EXT4_COMPR_FL 0x00000004
#define EXT4_SYNC_FL 0x00000008
#define EXT4_IMMUTABLE_FL 0x00000010
#define EXT4_APPEND_FL 0x00000020
#define EXT4_NODUMP_FL 0x00000040
#define EXT4_NOATIME_FL 0x00000080
#define EXT4_DIRTY_FL 0x00000100
#define EXT4_COMPRBLK_FL 0x00000200
#define EXT4_NOCOMPR_FL 0x00000400
#define EXT4_ENCRYPT_FL 0x00000800
#define EXT4_BTREE_FL 0x00001000
#define EXT4_INDEX_FL 0x00002000
#define EXT4_JOURNAL_DATA_FL 0x00004000
#define EXT4_NOTAIL_FL 0x00008000
#define EXT4_DIRSYNC_FL 0x00010000
#define EXT4_TOPDIR_FL 0x00020000
#define EXT4_HUGE_FILE_FL 0x00040000
#define EXT4_EXTENTS_FL 0x00080000
#define EXT4_VERITY_FL 0x00100000
#define EXT4_EA_INODE_FL 0x00200000
#define EXT4_RESERVED_FL 0x80000000
/* File type flags that are stored in the directory entries */
#define EXT4_FT_UNKNOWN 0
#define EXT4_FT_REG_FILE 1
#define EXT4_FT_DIR 2
#define EXT4_FT_CHRDEV 3
#define EXT4_FT_BLKDEV 4
#define EXT4_FT_FIFO 5
#define EXT4_FT_SOCK 6
#define EXT4_FT_SYMLINK 7
typedef struct {
UINT32 s_inodes_count;
UINT32 s_blocks_count;
UINT32 s_r_blocks_count;
UINT32 s_free_blocks_count;
UINT32 s_free_inodes_count;
UINT32 s_first_data_block;
UINT32 s_log_block_size;
UINT32 s_log_frag_size;
UINT32 s_blocks_per_group;
UINT32 s_frags_per_group;
UINT32 s_inodes_per_group;
UINT32 s_mtime;
UINT32 s_wtime;
UINT16 s_mnt_count;
UINT16 s_max_mnt_count;
UINT16 s_magic;
UINT16 s_state;
UINT16 s_errors;
UINT16 s_minor_rev_level;
UINT32 s_lastcheck;
UINT32 s_check_interval;
UINT32 s_creator_os;
UINT32 s_rev_level;
UINT16 s_def_resuid;
UINT16 s_def_resgid;
/* Every field after this comment is revision >= 1 */
UINT32 s_first_ino;
UINT16 s_inode_size;
UINT16 s_block_group_nr;
UINT32 s_feature_compat;
UINT32 s_feature_incompat;
UINT32 s_feature_ro_compat;
UINT8 s_uuid[16];
UINT8 s_volume_name[16];
UINT8 s_last_mounted[64];
UINT32 s_algo_bitmap;
UINT8 s_prealloc_blocks;
UINT8 s_prealloc_dir_blocks;
UINT16 unused;
UINT8 s_journal_uuid[16];
UINT32 s_journal_inum;
UINT32 s_journal_dev;
UINT32 s_last_orphan;
UINT32 s_hash_seed[4];
UINT8 s_def_hash_version;
UINT8 s_jnl_backup_type;
UINT16 s_desc_size;
UINT32 s_default_mount_options;
UINT32 s_first_meta_bg;
UINT32 s_mkfs_time;
UINT32 s_jnl_blocks[17];
UINT32 s_blocks_count_hi;
UINT32 s_r_blocks_count_hi;
UINT32 s_free_blocks_count_hi;
UINT16 s_min_extra_isize;
UINT16 s_want_extra_isize;
UINT32 s_flags;
UINT16 s_raid_stride;
UINT16 s_mmp_interval;
UINT64 s_mmp_block;
UINT32 s_raid_stride_width;
UINT8 s_log_groups_per_flex;
UINT8 s_checksum_type; // Only valid value is 1 - CRC32C
UINT16 s_reserved_pad;
UINT64 s_kbytes_written;
// Snapshot stuff isn't used in Linux and isn't implemented here
UINT32 s_snapshot_inum;
UINT32 s_snapshot_id;
UINT64 s_snapshot_r_blocks_count;
UINT32 s_snapshot_list;
UINT32 s_error_count;
UINT32 s_first_error_time;
UINT32 s_first_error_ino;
UINT64 s_first_error_block;
UINT8 s_first_error_func[32];
UINT32 s_first_error_line;
UINT32 s_last_error_time;
UINT32 s_last_error_ino;
UINT32 s_last_error_line;
UINT64 s_last_error_block;
UINT8 s_last_error_func[32];
UINT8 s_mount_opts[64];
UINT32 s_usr_quota_inum;
UINT32 s_grp_quota_inum;
UINT32 s_overhead_blocks;
UINT32 s_backup_bgs[2]; // sparse_super2
UINT8 s_encrypt_algos[4];
UINT8 s_encrypt_pw_salt[16];
UINT32 s_lpf_ino;
UINT32 s_prj_quota_inum;
UINT32 s_checksum_seed;
UINT32 s_reserved[98];
UINT32 s_checksum;
} EXT4_SUPERBLOCK;
STATIC_ASSERT (
sizeof (EXT4_SUPERBLOCK) == 1024,
"ext4 superblock struct has incorrect size"
);
typedef struct {
UINT32 bg_block_bitmap_lo;
UINT32 bg_inode_bitmap_lo;
UINT32 bg_inode_table_lo;
UINT16 bg_free_blocks_count_lo;
UINT16 bg_free_inodes_count_lo;
UINT16 bg_used_dirs_count_lo;
UINT16 bg_flags;
UINT32 bg_exclude_bitmap_lo;
UINT16 bg_block_bitmap_csum_lo;
UINT16 bg_inode_bitmap_csum_lo;
UINT16 bg_itable_unused_lo;
UINT16 bg_checksum;
UINT32 bg_block_bitmap_hi;
UINT32 bg_inode_bitmap_hi;
UINT32 bg_inode_table_hi;
UINT16 bg_free_blocks_count_hi;
UINT16 bg_free_inodes_count_hi;
UINT16 bg_used_dirs_count_hi;
UINT16 bg_itable_unused_hi;
UINT32 bg_exclude_bitmap_hi;
UINT16 bg_block_bitmap_csum_hi;
UINT16 bg_inode_bitmap_csum_hi;
UINT32 bg_reserved;
} EXT4_BLOCK_GROUP_DESC;
#define EXT4_OLD_BLOCK_DESC_SIZE 32
#define EXT4_64BIT_BLOCK_DESC_SIZE 64
STATIC_ASSERT (
sizeof (EXT4_BLOCK_GROUP_DESC) == EXT4_64BIT_BLOCK_DESC_SIZE,
"ext4 block group descriptor struct has incorrect size"
);
#define EXT4_DBLOCKS 12
#define EXT4_IND_BLOCK 12
#define EXT4_DIND_BLOCK 13
#define EXT4_TIND_BLOCK 14
#define EXT4_NR_BLOCKS 15
#define EXT4_FAST_SYMLINK_MAX_SIZE EXT4_NR_BLOCKS * sizeof(UINT32)
#define EXT4_GOOD_OLD_INODE_SIZE 128U
typedef struct _Ext4_I_OSD2_Linux {
UINT16 l_i_blocks_high;
UINT16 l_i_file_acl_high;
UINT16 l_i_uid_high;
UINT16 l_i_gid_high;
UINT16 l_i_checksum_lo;
UINT16 l_i_reserved;
} EXT4_OSD2_LINUX;
typedef struct _Ext4_I_OSD2_Hurd {
UINT16 h_i_reserved1;
UINT16 h_i_mode_high;
UINT16 h_i_uid_high;
UINT16 h_i_gid_high;
UINT32 h_i_author;
} EXT4_OSD2_HURD;
typedef union {
// Note: Toolchain-specific defines (such as "linux") stops us from using
// simpler names down here.
EXT4_OSD2_LINUX data_linux;
EXT4_OSD2_HURD data_hurd;
} EXT4_OSD2;
typedef struct _Ext4Inode {
UINT16 i_mode;
UINT16 i_uid;
UINT32 i_size_lo;
UINT32 i_atime;
UINT32 i_ctime;
UINT32 i_mtime;
UINT32 i_dtime;
UINT16 i_gid;
UINT16 i_links;
UINT32 i_blocks;
UINT32 i_flags;
UINT32 i_os_spec;
UINT32 i_data[EXT4_NR_BLOCKS];
UINT32 i_generation;
UINT32 i_file_acl;
UINT32 i_size_hi;
UINT32 i_faddr;
EXT4_OSD2 i_osd2;
UINT16 i_extra_isize;
UINT16 i_checksum_hi;
UINT32 i_ctime_extra;
UINT32 i_mtime_extra;
UINT32 i_atime_extra;
UINT32 i_crtime;
UINT32 i_crtime_extra;
UINT32 i_version_hi;
UINT32 i_projid;
} EXT4_INODE;
#define EXT4_NAME_MAX 255
typedef struct {
// offset 0x0: inode number (if 0, unused entry, should skip.)
UINT32 inode;
// offset 0x4: Directory entry's length.
// Note: rec_len >= name_len + EXT4_MIN_DIR_ENTRY_LEN and rec_len % 4 == 0.
UINT16 rec_len;
// offset 0x6: Directory entry's name's length
UINT8 name_len;
// offset 0x7: Directory entry's file type indicator
UINT8 file_type;
// offset 0x8: name[name_len]: Variable length character array; not null-terminated.
CHAR8 name[EXT4_NAME_MAX];
// Further notes on names:
// 1) We use EXT4_NAME_MAX here instead of flexible arrays for ease of use around the driver.
//
// 2) ext4 directories are defined, as the documentation puts it, as:
// "a directory is more or less a flat file that maps an arbitrary byte string
// (usually ASCII) to an inode number on the filesystem". So, they are not
// necessarily encoded with ASCII, UTF-8, or any of the sort. We must treat it
// as a bag of bytes. When interacting with EFI interfaces themselves (which expect UCS-2)
// we skip any directory entry that is not valid UTF-8.
} EXT4_DIR_ENTRY;
#define EXT4_MIN_DIR_ENTRY_LEN 8
// This on-disk structure is present at the bottom of the extent tree
typedef struct {
// First logical block
UINT32 ee_block;
// Length of the extent, in blocks
UINT16 ee_len;
// The physical (filesystem-relative) block is split between the high 16 bits
// and the low 32 bits - this forms a 48-bit block number
UINT16 ee_start_hi;
UINT32 ee_start_lo;
} EXT4_EXTENT;
// This on-disk structure is present at all levels except the bottom
typedef struct {
// This index covers logical blocks from 'ei_block'
UINT32 ei_block;
// Block of the next level of the extent tree, similarly split in a high and
// low portion.
UINT32 ei_leaf_lo;
UINT16 ei_leaf_hi;
UINT16 ei_unused;
} EXT4_EXTENT_INDEX;
typedef struct {
// Needs to be EXT4_EXTENT_HEADER_MAGIC
UINT16 eh_magic;
// Number of entries
UINT16 eh_entries;
// Maximum number of entries that could follow this header
UINT16 eh_max;
// Depth of this node in the tree - the tree can be at most 5 levels deep
UINT16 eh_depth;
// Unused by standard ext4
UINT32 eh_generation;
} EXT4_EXTENT_HEADER;
#define EXT4_EXTENT_HEADER_MAGIC 0xF30A
// Specified by ext4 docs and backed by a bunch of math
#define EXT4_EXTENT_TREE_MAX_DEPTH 5
typedef struct {
// CRC32C of UUID + inode number + igeneration + extent block
UINT32 eb_checksum;
} EXT4_EXTENT_TAIL;
/**
* EXT4 has this feature called uninitialized extents:
* An extent has a maximum of 32768 blocks (2^15 or 1 << 15).
* When we find an extent with > 32768 blocks, this extent is called
* uninitialized. Long story short, it's an extent that behaves as a file hole
* but has blocks already allocated.
*/
#define EXT4_EXTENT_MAX_INITIALIZED (1 << 15)
typedef UINT64 EXT4_BLOCK_NR;
typedef UINT32 EXT2_BLOCK_NR;
typedef UINT32 EXT4_INO_NR;
/* Special inode numbers */
#define EXT4_ROOT_INODE_NR 2
#define EXT4_USR_QUOTA_INODE_NR 3
#define EXT4_GRP_QUOTA_INODE_NR 4
#define EXT4_BOOT_LOADER_INODE_NR 5
#define EXT4_UNDEL_DIR_INODE_NR 6
#define EXT4_RESIZE_INODE_NR 7
#define EXT4_JOURNAL_INODE_NR 8
/* First non-reserved inode for old ext4 filesystems */
#define EXT4_GOOD_OLD_FIRST_INODE_NR 11
#define EXT4_BLOCK_FILE_HOLE 0
#endif

847
Ext4Pkg/Ext4Dxe/Ext4Dxe.c Normal file
View File

@ -0,0 +1,847 @@
/** @file
Driver entry point
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExt4DriverNameTable[] = {
{
"eng;en",
L"Ext4 File System Driver"
},
{
NULL,
NULL
}
};
GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExt4ControllerNameTable[] = {
{
"eng;en",
L"Ext4 File System"
},
{
NULL,
NULL
}
};
// Needed by gExt4ComponentName*
/**
Retrieves a Unicode string that is the user-readable name of the EFI Driver.
@param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
@param[in] Language A pointer to a three-character ISO 639-2 language identifier.
This is the language of the driver name that that the caller
is requesting, and it must match one of the languages specified
in SupportedLanguages. The number of languages supported by a
driver is up to the driver writer.
@param[out] DriverName A pointer to the Unicode string to return. This Unicode string
is the name of the driver specified by This in the language
specified by Language.
@retval EFI_SUCCESS The Unicode string for the Driver specified by This
and the language specified by Language was returned
in DriverName.
@retval EFI_INVALID_PARAMETER Language is NULL.
@retval EFI_INVALID_PARAMETER DriverName is NULL.
@retval EFI_UNSUPPORTED The driver specified by This does not support the
language specified by Language.
**/
EFI_STATUS
EFIAPI
Ext4ComponentNameGetDriverName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
);
/**
Retrieves a Unicode string that is the user readable name of the controller
that is being managed by an EFI Driver.
@param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
@param[in] ControllerHandle The handle of a controller that the driver specified by
This is managing. This handle specifies the controller
whose name is to be returned.
@param[in] ChildHandle The handle of the child controller to retrieve the name
of. This is an optional parameter that may be NULL. It
will be NULL for device drivers. It will also be NULL
for a bus drivers that wish to retrieve the name of the
bus controller. It will not be NULL for a bus driver
that wishes to retrieve the name of a child controller.
@param[in] Language A pointer to a three character ISO 639-2 language
identifier. This is the language of the controller name
that the caller is requesting, and it must match one
of the languages specified in SupportedLanguages. The
number of languages supported by a driver is up to the
driver writer.
@param[out] ControllerName A pointer to the Unicode string to return. This Unicode
string is the name of the controller specified by
ControllerHandle and ChildHandle in the language specified
by Language, from the point of view of the driver specified
by This.
@retval EFI_SUCCESS The Unicode string for the user-readable name in the
language specified by Language for the driver
specified by This was returned in DriverName.
@retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
@retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE.
@retval EFI_INVALID_PARAMETER Language is NULL.
@retval EFI_INVALID_PARAMETER ControllerName is NULL.
@retval EFI_UNSUPPORTED The driver specified by This is not currently managing
the controller specified by ControllerHandle and
ChildHandle.
@retval EFI_UNSUPPORTED The driver specified by This does not support the
language specified by Language.
**/
EFI_STATUS
EFIAPI
Ext4ComponentNameGetControllerName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle OPTIONAL,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
);
extern EFI_COMPONENT_NAME_PROTOCOL gExt4ComponentName;
GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gExt4ComponentName = {
Ext4ComponentNameGetDriverName,
Ext4ComponentNameGetControllerName,
"eng"
};
//
// EFI Component Name 2 Protocol
//
GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gExt4ComponentName2 = {
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME)Ext4ComponentNameGetDriverName,
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)Ext4ComponentNameGetControllerName,
"en"
};
// Needed by gExt4BindingProtocol
/**
Tests to see if this driver supports a given controller. If a child device is provided,
it further tests to see if this driver supports creating a handle for the specified child device.
This function checks to see if the driver specified by This supports the device specified by
ControllerHandle. Drivers will typically use the device path attached to
ControllerHandle and/or the services from the bus I/O abstraction attached to
ControllerHandle to determine if the driver supports ControllerHandle. This function
may be called many times during platform initialization. In order to reduce boot times, the tests
performed by this function must be very small, and take as little time as possible to execute. This
function must not change the state of any hardware devices, and this function must be aware that the
device specified by ControllerHandle may already be managed by the same driver or a
different driver. This function must match its calls to AllocatePages() with FreePages(),
AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
Because ControllerHandle may have been previously started by the same driver, if a protocol is
already in the opened state, then it must not be closed with CloseProtocol(). This is required
to guarantee the state of ControllerHandle is not modified by this function.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the controller to test. This handle
must support a protocol interface that supplies
an I/O abstraction to the driver.
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
parameter is ignored by device drivers, and is optional for bus
drivers. For bus drivers, if this parameter is not NULL, then
the bus driver must determine if the bus controller specified
by ControllerHandle and the child controller specified
by RemainingDevicePath are both supported by this
bus driver.
@retval EFI_SUCCESS The device specified by ControllerHandle and
RemainingDevicePath is supported by the driver specified by This.
@retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
RemainingDevicePath is already being managed by the driver
specified by This.
@retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
RemainingDevicePath is already being managed by a different
driver or an application that requires exclusive access.
Currently not implemented.
@retval EFI_UNSUPPORTED The device specified by ControllerHandle and
RemainingDevicePath is not supported by the driver specified by This.
**/
EFI_STATUS
EFIAPI
Ext4IsBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL
);
/**
Starts a device controller or a bus controller.
The Start() function is designed to be invoked from the EFI boot service ConnectController().
As a result, much of the error checking on the parameters to Start() has been moved into this
common boot service. It is legal to call Start() from other locations,
but the following calling restrictions must be followed, or the system behavior will not be deterministic.
1. ControllerHandle must be a valid EFI_HANDLE.
2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
EFI_DEVICE_PATH_PROTOCOL.
3. Prior to calling Start(), the Supported() function for the driver specified by This must
have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the controller to start. This handle
must support a protocol interface that supplies
an I/O abstraction to the driver.
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
parameter is ignored by device drivers, and is optional for bus
drivers. For a bus driver, if this parameter is NULL, then handles
for all the children of Controller are created by this driver.
If this parameter is not NULL and the first Device Path Node is
not the End of Device Path Node, then only the handle for the
child device specified by the first Device Path Node of
RemainingDevicePath is created by this driver.
If the first Device Path Node of RemainingDevicePath is
the End of Device Path Node, no child handle is created by this
driver.
@retval EFI_SUCCESS The device was started.
@retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
@retval Others The driver failded to start the device.
**/
EFI_STATUS
EFIAPI
Ext4Bind (
IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL
);
/**
Stops a device controller or a bus controller.
The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
As a result, much of the error checking on the parameters to Stop() has been moved
into this common boot service. It is legal to call Stop() from other locations,
but the following calling restrictions must be followed, or the system behavior will not be deterministic.
1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
same driver's Start() function.
2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
EFI_HANDLE. In addition, all of these handles must have been created in this driver's
Start() function, and the Start() function must have called OpenProtocol() on
ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle A handle to the device being stopped. The handle must
support a bus specific I/O protocol for the driver
to use to stop the device.
@param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
@param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
if NumberOfChildren is 0.
@retval EFI_SUCCESS The device was stopped.
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
**/
EFI_STATUS
EFIAPI
Ext4Stop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
);
EFI_DRIVER_BINDING_PROTOCOL gExt4BindingProtocol =
{
.Supported = Ext4IsBindingSupported,
.Start = Ext4Bind,
.Stop = Ext4Stop,
.Version = EXT4_DRIVER_VERSION,
.ImageHandle = NULL,
.DriverBindingHandle = NULL
};
/**
Retrieves a Unicode string that is the user readable name of the controller
that is being managed by an EFI Driver.
@param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
@param[in] ControllerHandle The handle of a controller that the driver specified by
This is managing. This handle specifies the controller
whose name is to be returned.
@param[in] ChildHandle The handle of the child controller to retrieve the name
of. This is an optional parameter that may be NULL. It
will be NULL for device drivers. It will also be NULL
for a bus drivers that wish to retrieve the name of the
bus controller. It will not be NULL for a bus driver
that wishes to retrieve the name of a child controller.
@param[in] Language A pointer to a three character ISO 639-2 language
identifier. This is the language of the controller name
that the caller is requesting, and it must match one
of the languages specified in SupportedLanguages. The
number of languages supported by a driver is up to the
driver writer.
@param[out] ControllerName A pointer to the Unicode string to return. This Unicode
string is the name of the controller specified by
ControllerHandle and ChildHandle in the language specified
by Language, from the point of view of the driver specified
by This.
@retval EFI_SUCCESS The Unicode string for the user-readable name in the
language specified by Language for the driver
specified by This was returned in DriverName.
@retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
@retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE.
@retval EFI_INVALID_PARAMETER Language is NULL.
@retval EFI_INVALID_PARAMETER ControllerName is NULL.
@retval EFI_UNSUPPORTED The driver specified by This is not currently managing
the controller specified by ControllerHandle and
ChildHandle.
@retval EFI_UNSUPPORTED The driver specified by This does not support the
language specified by Language.
**/
EFI_STATUS
EFIAPI
Ext4ComponentNameGetControllerName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle OPTIONAL,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
)
{
EFI_STATUS Status;
if (ChildHandle != NULL) {
return EFI_UNSUPPORTED;
}
// Test if the driver manages ControllHandle
Status = EfiTestManagedDevice (
ControllerHandle,
gExt4BindingProtocol.DriverBindingHandle,
&gEfiDiskIoProtocolGuid
);
if (EFI_ERROR (Status)) {
return Status;
}
return LookupUnicodeString2 (
Language,
This->SupportedLanguages,
mExt4ControllerNameTable,
ControllerName,
(BOOLEAN)(This == &gExt4ComponentName)
);
}
/**
Retrieves a Unicode string that is the user-readable name of the EFI Driver.
@param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
@param[in] Language A pointer to a three-character ISO 639-2 language identifier.
This is the language of the driver name that that the caller
is requesting, and it must match one of the languages specified
in SupportedLanguages. The number of languages supported by a
driver is up to the driver writer.
@param[out] DriverName A pointer to the Unicode string to return. This Unicode string
is the name of the driver specified by This in the language
specified by Language.
@retval EFI_SUCCESS The Unicode string for the Driver specified by This
and the language specified by Language was returned
in DriverName.
@retval EFI_INVALID_PARAMETER Language is NULL.
@retval EFI_INVALID_PARAMETER DriverName is NULL.
@retval EFI_UNSUPPORTED The driver specified by This does not support the
language specified by Language.
**/
EFI_STATUS
EFIAPI
Ext4ComponentNameGetDriverName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
)
{
return LookupUnicodeString2 (
Language,
This->SupportedLanguages,
mExt4DriverNameTable,
DriverName,
(BOOLEAN)(This == &gExt4ComponentName)
);
}
/**
Stops a device controller or a bus controller.
The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
As a result, much of the error checking on the parameters to Stop() has been moved
into this common boot service. It is legal to call Stop() from other locations,
but the following calling restrictions must be followed, or the system behavior will not be deterministic.
1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
same driver's Start() function.
2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
EFI_HANDLE. In addition, all of these handles must have been created in this driver's
Start() function, and the Start() function must have called OpenProtocol() on
ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle A handle to the device being stopped. The handle must
support a bus specific I/O protocol for the driver
to use to stop the device.
@param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
@param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
if NumberOfChildren is 0.
@retval EFI_SUCCESS The device was stopped.
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
**/
EFI_STATUS
EFIAPI
Ext4Stop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
EFI_STATUS Status;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Sfs;
EXT4_PARTITION *Partition;
BOOLEAN HasDiskIo2;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiSimpleFileSystemProtocolGuid,
(VOID **)&Sfs,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
Partition = (EXT4_PARTITION *)Sfs;
HasDiskIo2 = EXT4_DISK_IO2 (Partition) != NULL;
Status = Ext4UnmountAndFreePartition (Partition);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->UninstallMultipleProtocolInterfaces (
ControllerHandle,
&gEfiSimpleFileSystemProtocolGuid,
&Partition->Interface,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
// Close all open protocols (DiskIo, DiskIo2, BlockIo)
Status = gBS->CloseProtocol (
ControllerHandle,
&gEfiDiskIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->CloseProtocol (
ControllerHandle,
&gEfiBlockIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
if (HasDiskIo2) {
Status = gBS->CloseProtocol (
ControllerHandle,
&gEfiDiskIo2ProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
}
return Status;
}
/**
Ext4Dxe Driver's entry point.
Called at load time.
@param[in] ImageHandle Handle to the image.
@param[in] SystemTable Pointer to the EFI_SYSTEM_TABLE.
@return Result of the load.
**/
EFI_STATUS
EFIAPI
Ext4EntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return EfiLibInstallAllDriverProtocols2 (
ImageHandle,
SystemTable,
&gExt4BindingProtocol,
ImageHandle,
&gExt4ComponentName,
&gExt4ComponentName2,
NULL,
NULL,
NULL,
NULL
);
}
/**
Ext4Dxe Driver's unload callback.
Called at unload time.
@param[in] ImageHandle Handle to the image.
@return Result of the unload.
**/
EFI_STATUS
EFIAPI
Ext4Unload (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_HANDLE *DeviceHandleBuffer;
UINTN DeviceHandleCount;
UINTN Index;
EFI_HANDLE Handle;
Status = gBS->LocateHandleBuffer (
AllHandles,
NULL,
NULL,
&DeviceHandleCount,
&DeviceHandleBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
for (Index = 0; Index < DeviceHandleCount; Index++) {
Handle = DeviceHandleBuffer[Index];
Status = EfiTestManagedDevice (Handle, ImageHandle, &gEfiDiskIoProtocolGuid);
if (Status == EFI_SUCCESS) {
Status = gBS->DisconnectController (Handle, ImageHandle, NULL);
if (EFI_ERROR (Status)) {
break;
}
}
}
FreePool (DeviceHandleBuffer);
Status = EfiLibUninstallAllDriverProtocols2 (
&gExt4BindingProtocol,
&gExt4ComponentName,
&gExt4ComponentName2,
NULL,
NULL,
NULL,
NULL
);
return Status;
}
/**
Tests to see if this driver supports a given controller. If a child device is provided,
it further tests to see if this driver supports creating a handle for the specified child device.
This function checks to see if the driver specified by This supports the device specified by
ControllerHandle. Drivers will typically use the device path attached to
ControllerHandle and/or the services from the bus I/O abstraction attached to
ControllerHandle to determine if the driver supports ControllerHandle. This function
may be called many times during platform initialization. In order to reduce boot times, the tests
performed by this function must be very small, and take as little time as possible to execute. This
function must not change the state of any hardware devices, and this function must be aware that the
device specified by ControllerHandle may already be managed by the same driver or a
different driver. This function must match its calls to AllocatePages() with FreePages(),
AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
Because ControllerHandle may have been previously started by the same driver, if a protocol is
already in the opened state, then it must not be closed with CloseProtocol(). This is required
to guarantee the state of ControllerHandle is not modified by this function.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the controller to test. This handle
must support a protocol interface that supplies
an I/O abstraction to the driver.
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
parameter is ignored by device drivers, and is optional for bus
drivers. For bus drivers, if this parameter is not NULL, then
the bus driver must determine if the bus controller specified
by ControllerHandle and the child controller specified
by RemainingDevicePath are both supported by this
bus driver.
@retval EFI_SUCCESS The device specified by ControllerHandle and
RemainingDevicePath is supported by the driver specified by This.
@retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
RemainingDevicePath is already being managed by the driver
specified by This.
@retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
RemainingDevicePath is already being managed by a different
driver or an application that requires exclusive access.
@retval EFI_UNSUPPORTED The device specified by ControllerHandle and
RemainingDevicePath is not supported by the driver specified by This.
**/
EFI_STATUS
EFIAPI
Ext4IsBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
EFI_DISK_IO_PROTOCOL *DiskIo;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
DiskIo = NULL;
BlockIo = NULL;
//
// Open the IO Abstraction(s) needed to perform the supported test
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDiskIoProtocolGuid,
(VOID **)&DiskIo,
BindingProtocol->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open the IO Abstraction(s) needed to perform the supported test
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiBlockIoProtocolGuid,
(VOID **)&BlockIo,
BindingProtocol->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
if (!Ext4SuperblockCheckMagic (DiskIo, BlockIo)) {
Status = EFI_UNSUPPORTED;
}
}
//
// Close the I/O Abstraction(s) used to perform the supported test
//
if (DiskIo != NULL) {
gBS->CloseProtocol (
ControllerHandle,
&gEfiDiskIoProtocolGuid,
BindingProtocol->DriverBindingHandle,
ControllerHandle
);
}
if (BlockIo != NULL) {
gBS->CloseProtocol (
ControllerHandle,
&gEfiBlockIoProtocolGuid,
BindingProtocol->DriverBindingHandle,
ControllerHandle
);
}
return Status;
}
/**
Starts a device controller or a bus controller.
The Start() function is designed to be invoked from the EFI boot service ConnectController().
As a result, much of the error checking on the parameters to Start() has been moved into this
common boot service. It is legal to call Start() from other locations,
but the following calling restrictions must be followed, or the system behavior will not be deterministic.
1. ControllerHandle must be a valid EFI_HANDLE.
2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
EFI_DEVICE_PATH_PROTOCOL.
3. Prior to calling Start(), the Supported() function for the driver specified by This must
have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the controller to start. This handle
must support a protocol interface that supplies
an I/O abstraction to the driver.
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
parameter is ignored by device drivers, and is optional for bus
drivers. For a bus driver, if this parameter is NULL, then handles
for all the children of Controller are created by this driver.
If this parameter is not NULL and the first Device Path Node is
not the End of Device Path Node, then only the handle for the
child device specified by the first Device Path Node of
RemainingDevicePath is created by this driver.
If the first Device Path Node of RemainingDevicePath is
the End of Device Path Node, no child handle is created by this
driver.
@retval EFI_SUCCESS The device was started.
@retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
@retval Others The driver failded to start the device.
**/
EFI_STATUS
EFIAPI
Ext4Bind (
IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL
)
{
EFI_DISK_IO_PROTOCOL *DiskIo;
EFI_DISK_IO2_PROTOCOL *DiskIo2;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
EFI_STATUS Status;
DiskIo2 = NULL;
BlockIo = NULL;
DiskIo = NULL;
// Note: We initialize collation here since this is called in BDS, when we are likely
// to have the Unicode Collation protocols available.
Status = Ext4InitialiseUnicodeCollation (BindingProtocol->ImageHandle);
if (EFI_ERROR (Status)) {
// Lets throw a loud error into the log
// It is very unlikely something like this may fire out of the blue. Chances are either
// the platform configuration is wrong, or we are.
DEBUG ((DEBUG_ERROR, "[ext4] Error: Unicode Collation not available - failure to Start() - error %r\n", Status));
goto Error;
}
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDiskIoProtocolGuid,
(VOID **)&DiskIo,
BindingProtocol->ImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
goto Error;
}
DEBUG ((DEBUG_INFO, "[ext4] Controller supports DISK_IO\n"));
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDiskIo2ProtocolGuid,
(VOID **)&DiskIo2,
BindingProtocol->ImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
// It's okay to not support DISK_IO2
if (DiskIo2 != NULL) {
DEBUG ((DEBUG_INFO, "[ext4] Controller supports DISK_IO2\n"));
}
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiBlockIoProtocolGuid,
(VOID **)&BlockIo,
BindingProtocol->ImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Ext4OpenPartition (ControllerHandle, DiskIo, DiskIo2, BlockIo);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "[ext4] Error mounting: %r\n", Status));
// Falls through to Error
} else {
return Status;
}
Error:
if (DiskIo) {
gBS->CloseProtocol (
ControllerHandle,
&gEfiDiskIoProtocolGuid,
BindingProtocol->ImageHandle,
ControllerHandle
);
}
if (DiskIo2) {
gBS->CloseProtocol (
ControllerHandle,
&gEfiDiskIo2ProtocolGuid,
BindingProtocol->ImageHandle,
ControllerHandle
);
}
if (BlockIo) {
gBS->CloseProtocol (
ControllerHandle,
&gEfiBlockIoProtocolGuid,
BindingProtocol->ImageHandle,
ControllerHandle
);
}
return Status;
}

1255
Ext4Pkg/Ext4Dxe/Ext4Dxe.h Normal file

File diff suppressed because it is too large Load Diff

149
Ext4Pkg/Ext4Dxe/Ext4Dxe.inf Normal file
View File

@ -0,0 +1,149 @@
## @file
# Ext4 Package
#
# UEFI Driver that produces the Simple File System Protocol for a partition that is formatted
# with the EXT4 file system.
# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
#
# Copyright (c) 2021 Pedro Falcato
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
# Layout of an EXT2/3/4 filesystem:
# (note: this driver has been developed using
# https://www.kernel.org/doc/html/latest/filesystems/ext4/index.html as
# documentation).
#
# An ext2/3/4 filesystem (here on out referred to as simply an ext4 filesystem,
# due to the similarities) is composed of various concepts:
#
# 1) Superblock
# The superblock is the structure near (1024 bytes offset from the start)
# the start of the partition, and describes the filesystem in general.
# Here, we get to know the size of the filesystem's blocks, which features
# it supports or not, whether it's been cleanly unmounted, how many blocks
# we have, etc.
#
# 2) Block groups
# EXT4 filesystems are divided into block groups, and each block group covers
# s_blocks_per_group(8 * Block Size) blocks. Each block group has an
# associated block group descriptor; these are present directly after the
# superblock. Each block group descriptor contains the location of the
# inode table, and the inode and block bitmaps (note these bitmaps are only
# a block long, which gets us the 8 * Block Size formula covered previously).
#
# 3) Blocks
# The ext4 filesystem is divided in blocks, of size s_log_block_size ^ 1024.
# Blocks can be allocated using individual block groups's bitmaps. Note
# that block 0 is invalid and its presence on extents/block tables means
# it's part of a file hole, and that particular location must be read as
# a block full of zeros.
#
# 4) Inodes
# The ext4 filesystem divides files/directories into inodes (originally
# index nodes). Each file/socket/symlink/directory/etc (here on out referred
# to as a file, since there is no distinction under the ext4 filesystem) is
# stored as a /nameless/ inode, that is stored in some block group's inode
# table. Each inode has s_inode_size size (or GOOD_OLD_INODE_SIZE if it's
# an old filesystem), and holds various metadata about the file. Since the
# largest inode structure right now is ~160 bytes, the rest of the inode
# contains inline extended attributes. Inodes' data is stored using either
# data blocks (under ext2/3) or extents (under ext4).
#
# 5) Extents
# Ext4 inodes store data in extents. These let N contiguous logical blocks
# that are represented by N contiguous physical blocks be represented by a
# single extent structure, which minimizes filesystem metadata bloat and
# speeds up block mapping (particularly due to the fact that high-quality
# ext4 implementations like linux's try /really/ hard to make the file
# contiguous, so it's common to have files with almost 0 fragmentation).
# Inodes that use extents store them in a tree, and the top of the tree
# is stored on i_data. The tree's leaves always start with an
# EXT4_EXTENT_HEADER and contain EXT4_EXTENT_INDEX on eh_depth != 0 and
# EXT4_EXTENT on eh_depth = 0; these entries are always sorted by logical
# block.
#
# 6) Directories
# Ext4 directories are files that store name -> inode mappings for the
# logical directory; this is where files get their names, which means ext4
# inodes do not themselves have names, since they can be linked (present)
# multiple times with different names. Directories can store entries in two
# different ways:
# 1) Classical linear directories: They store entries as a mostly-linked
# mostly-list of EXT4_DIR_ENTRY.
# 2) Hash tree directories: These are used for larger directories, with
# hundreds of entries, and are designed in a backwards compatible way.
# These are not yet implemented in the Ext4Dxe driver.
#
# 7) Journal
# Ext3/4 filesystems have a journal to help protect the filesystem against
# system crashes. This is not yet implemented in Ext4Dxe but is described
# in detail in the Linux kernel's documentation.
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = Ext4Dxe
MODULE_UNI_FILE = Ext4Dxe.uni
FILE_GUID = 75F2B676-D73B-45CB-B7C1-303C7F4E6FD6
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = Ext4EntryPoint
UNLOAD_IMAGE = Ext4Unload
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 EBC
#
[Sources]
Ext4Dxe.c
Partition.c
DiskUtil.c
Superblock.c
BlockGroup.c
Inode.c
Directory.c
Extents.c
File.c
Symlink.c
Collation.c
Ext4Disk.h
Ext4Dxe.h
BlockMap.c
[Packages]
MdePkg/MdePkg.dec
RedfishPkg/RedfishPkg.dec
[LibraryClasses]
UefiRuntimeServicesTableLib
UefiBootServicesTableLib
MemoryAllocationLib
BaseMemoryLib
BaseLib
UefiLib
UefiDriverEntryPoint
DebugLib
PcdLib
OrderedCollectionLib
BaseUcs2Utf8Lib
[Guids]
gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED
gEfiFileSystemInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED
gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## UNDEFINED
[Protocols]
gEfiDiskIoProtocolGuid ## TO_START
gEfiDiskIo2ProtocolGuid ## TO_START
gEfiBlockIoProtocolGuid ## TO_START
gEfiSimpleFileSystemProtocolGuid ## BY_START
gEfiUnicodeCollationProtocolGuid ## TO_START
gEfiUnicodeCollation2ProtocolGuid ## TO_START
[Pcd]
gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang ## SOMETIMES_CONSUMES
gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang ## SOMETIMES_CONSUMES

View File

@ -0,0 +1,15 @@
## @file
# Ext4 Package
#
# UEFI Driver that produces the Simple File System Protocol for a partition that is formatted
# with the EXT4 file system.
# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
#
# Copyright (c) 2021 Pedro Falcato
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
#string STR_MODULE_ABSTRACT #language en-US "UEFI driver for the EXT4 file system."
#string STR_MODULE_DESCRIPTION #language en-US "Produces the EFI Simple File System protocol."

685
Ext4Pkg/Ext4Dxe/Extents.c Normal file
View File

@ -0,0 +1,685 @@
/** @file
Extent related routines
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
/**
Checks if the checksum of the extent data block is correct.
@param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER.
@param[in] File Pointer to the file.
@return TRUE if the checksum is correct, FALSE if there is corruption.
**/
BOOLEAN
Ext4CheckExtentChecksum (
IN CONST EXT4_EXTENT_HEADER *ExtHeader,
IN CONST EXT4_FILE *File
);
/**
Calculates the checksum of the extent data block.
@param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER.
@param[in] File Pointer to the file.
@return The checksum.
**/
UINT32
Ext4CalculateExtentChecksum (
IN CONST EXT4_EXTENT_HEADER *ExtHeader,
IN CONST EXT4_FILE *File
);
/**
Caches a range of extents, by allocating pool memory for each extent and adding it to the tree.
@param[in] File Pointer to the open file.
@param[in] Extents Pointer to an array of extents.
@param[in] NumberExtents Length of the array.
@return Result of the caching
**/
STATIC
EFI_STATUS
Ext4CacheExtents (
IN EXT4_FILE *File,
IN CONST EXT4_EXTENT *Extents,
IN UINT16 NumberExtents
);
/**
Gets an extent from the extents cache of the file.
@param[in] File Pointer to the open file.
@param[in] Block Block we want to grab.
@return Pointer to the extent, or NULL if it was not found.
**/
EXT4_EXTENT *
Ext4GetExtentFromMap (
IN EXT4_FILE *File,
IN UINT32 Block
);
/**
Retrieves the pointer to the top of the extent tree.
@param[in] Inode Pointer to the inode structure.
@return Pointer to an EXT4_EXTENT_HEADER. This pointer is inside
the inode and must not be freed.
**/
STATIC
EXT4_EXTENT_HEADER *
Ext4GetInoExtentHeader (
IN EXT4_INODE *Inode
)
{
return (EXT4_EXTENT_HEADER *)Inode->i_data;
}
/**
Checks if an extent header is valid.
@param[in] Header Pointer to the EXT4_EXTENT_HEADER structure.
@param[in] MaxEntries Maximum number of entries possible for this tree node.
@return TRUE if valid, FALSE if not.
**/
STATIC
BOOLEAN
Ext4ExtentHeaderValid (
IN CONST EXT4_EXTENT_HEADER *Header,
IN UINT16 MaxEntries
)
{
if (Header->eh_depth > EXT4_EXTENT_TREE_MAX_DEPTH) {
DEBUG ((DEBUG_ERROR, "[ext4] Invalid extent header depth %u\n", Header->eh_depth));
return FALSE;
}
if (Header->eh_magic != EXT4_EXTENT_HEADER_MAGIC) {
DEBUG ((DEBUG_ERROR, "[ext4] Invalid extent header magic %x\n", Header->eh_magic));
return FALSE;
}
// Note: We do not need to check eh_entries here, as the next branch makes sure max >= entries
if (Header->eh_max > MaxEntries) {
DEBUG ((
DEBUG_ERROR,
"[ext4] Invalid extent header max entries (%u eh_max, "
"theoretical max is %u) (larger than permitted)\n",
Header->eh_max,
MaxEntries
));
return FALSE;
}
if (Header->eh_max < Header->eh_entries) {
DEBUG ((
DEBUG_ERROR,
"[ext4] Invalid extent header num entries %u max entries %u\n",
Header->eh_entries,
Header->eh_max
));
return FALSE;
}
return TRUE;
}
/**
Performs a binary search for a EXT4_EXTENT_INDEX that corresponds to a
logical block in a given extent tree node.
@param[in] Header Pointer to the EXT4_EXTENT_HEADER structure.
@param[in] LogicalBlock Block that will be searched
@return Pointer to the found EXT4_EXTENT_INDEX.
**/
STATIC
EXT4_EXTENT_INDEX *
Ext4BinsearchExtentIndex (
IN EXT4_EXTENT_HEADER *Header,
IN EXT4_BLOCK_NR LogicalBlock
)
{
EXT4_EXTENT_INDEX *l;
EXT4_EXTENT_INDEX *r;
EXT4_EXTENT_INDEX *m;
l = ((EXT4_EXTENT_INDEX *)(Header + 1)) + 1;
r = ((EXT4_EXTENT_INDEX *)(Header + 1)) + Header->eh_entries - 1;
// Perform a mostly-standard binary search on the array
// This works very nicely because the extents arrays are always sorted.
while (l <= r) {
m = l + (r - l) / 2;
if (LogicalBlock < m->ei_block) {
r = m - 1;
} else {
l = m + 1;
}
}
return l - 1;
}
/**
Performs a binary search for a EXT4_EXTENT that corresponds to a
logical block in a given extent tree node.
@param[in] Header Pointer to the EXT4_EXTENT_HEADER structure.
@param[in] LogicalBlock Block that will be searched
@return Pointer to the found EXT4_EXTENT_INDEX, else NULL if the array is empty.
Note: The caller must check if the logical block
is actually mapped under the given extent.
**/
STATIC
EXT4_EXTENT *
Ext4BinsearchExtentExt (
IN EXT4_EXTENT_HEADER *Header,
IN EXT4_BLOCK_NR LogicalBlock
)
{
EXT4_EXTENT *l;
EXT4_EXTENT *r;
EXT4_EXTENT *m;
l = ((EXT4_EXTENT *)(Header + 1)) + 1;
r = ((EXT4_EXTENT *)(Header + 1)) + Header->eh_entries - 1;
// Perform a mostly-standard binary search on the array
// This works very nicely because the extents arrays are always sorted.
// Empty array
if (Header->eh_entries == 0) {
return NULL;
}
while (l <= r) {
m = l + (r - l) / 2;
if (LogicalBlock < m->ee_block) {
r = m - 1;
} else {
l = m + 1;
}
}
return l - 1;
}
/**
Retrieves the leaf block from an EXT4_EXTENT_INDEX.
@param[in] Index Pointer to the EXT4_EXTENT_INDEX structure.
@return Block number of the leaf node.
**/
STATIC
EXT4_BLOCK_NR
Ext4ExtentIdxLeafBlock (
IN EXT4_EXTENT_INDEX *Index
)
{
return LShiftU64 (Index->ei_leaf_hi, 32) | Index->ei_leaf_lo;
}
// Results of sizeof(i_data) / sizeof(extent) - 1 = 4
#define EXT4_NR_INLINE_EXTENTS 4
/**
Retrieves an extent from an EXT4 inode.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] File Pointer to the opened file.
@param[in] LogicalBlock Block number which the returned extent must cover.
@param[out] Extent Pointer to the output buffer, where the extent will be copied to.
@retval EFI_SUCCESS Retrieval was successful.
@retval EFI_NO_MAPPING Block has no mapping.
**/
EFI_STATUS
Ext4GetExtent (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
IN EXT4_BLOCK_NR LogicalBlock,
OUT EXT4_EXTENT *Extent
)
{
EXT4_INODE *Inode;
VOID *Buffer;
EXT4_EXTENT *Ext;
UINT32 CurrentDepth;
EXT4_EXTENT_HEADER *ExtHeader;
EXT4_EXTENT_INDEX *Index;
EFI_STATUS Status;
UINT16 MaxExtentsPerNode;
EXT4_BLOCK_NR BlockNumber;
Inode = File->Inode;
Ext = NULL;
Buffer = NULL;
DEBUG ((DEBUG_FS, "[ext4] Looking up extent for block %lu\n", LogicalBlock));
// ext4 does not have support for logical block numbers bigger than UINT32_MAX
if (LogicalBlock > (UINT32)-1) {
return EFI_NO_MAPPING;
}
// Note: Right now, holes are the single biggest reason for cache misses
// We should find a way to get (or cache) holes
if ((Ext = Ext4GetExtentFromMap (File, (UINT32)LogicalBlock)) != NULL) {
*Extent = *Ext;
return EFI_SUCCESS;
}
if ((Inode->i_flags & EXT4_EXTENTS_FL) == 0) {
// If this is an older ext2/ext3 filesystem, emulate Ext4GetExtent using the block map
// By specification files using block maps are limited to 2^32 blocks,
// so we can safely cast LogicalBlock to uint32
Status = Ext4GetBlocks (Partition, File, (UINT32)LogicalBlock, Extent);
if (!EFI_ERROR (Status)) {
Status = Ext4CacheExtents (File, Extent, 1);
if (EFI_ERROR (Status) && (Status != EFI_OUT_OF_RESOURCES)) {
return Status;
}
Status = EFI_SUCCESS;
}
return Status;
}
// Slow path, we'll need to read from disk and (try to) cache those extents.
ExtHeader = Ext4GetInoExtentHeader (Inode);
if (!Ext4ExtentHeaderValid (ExtHeader, EXT4_NR_INLINE_EXTENTS)) {
return EFI_VOLUME_CORRUPTED;
}
CurrentDepth = ExtHeader->eh_depth;
// A single node fits into a single block, so we can only have (BlockSize / sizeof(EXT4_EXTENT)) - 1
// extents in a single node. Note the -1, because both leaf and internal node headers are 12 bytes,
// and so are individual entries.
MaxExtentsPerNode = (UINT16)((Partition->BlockSize / sizeof (EXT4_EXTENT)) - 1);
while (ExtHeader->eh_depth != 0) {
CurrentDepth--;
// While depth != 0, we're traversing the tree itself and not any leaves
// As such, every entry is an EXT4_EXTENT_INDEX entry
// Note: Entries after the extent header, either index or actual extent, are always sorted.
// Therefore, we can use binary search, and it's actually the standard for doing so
// (see FreeBSD).
Index = Ext4BinsearchExtentIndex (ExtHeader, LogicalBlock);
BlockNumber = Ext4ExtentIdxLeafBlock (Index);
// Check that block isn't file hole
if (BlockNumber == EXT4_BLOCK_FILE_HOLE) {
if (Buffer != NULL) {
FreePool (Buffer);
}
return EFI_VOLUME_CORRUPTED;
}
if (Buffer == NULL) {
Buffer = AllocatePool (Partition->BlockSize);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
}
// Read the leaf block onto the previously-allocated buffer.
Status = Ext4ReadBlocks (Partition, Buffer, 1, BlockNumber);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
ExtHeader = Buffer;
if (!Ext4ExtentHeaderValid (ExtHeader, MaxExtentsPerNode)) {
FreePool (Buffer);
return EFI_VOLUME_CORRUPTED;
}
if (!Ext4CheckExtentChecksum (ExtHeader, File)) {
DEBUG ((DEBUG_ERROR, "[ext4] Invalid extent checksum\n"));
FreePool (Buffer);
return EFI_VOLUME_CORRUPTED;
}
if (ExtHeader->eh_depth != CurrentDepth) {
FreePool (Buffer);
return EFI_VOLUME_CORRUPTED;
}
}
/* We try to cache every extent under a single leaf, since it's quite likely that we
* may need to access things sequentially. Furthermore, ext4 block allocation as done
* by linux (and possibly other systems) is quite fancy and usually it results in a small number of extents.
* Therefore, we shouldn't have any memory issues.
**/
Status = Ext4CacheExtents (File, (EXT4_EXTENT *)(ExtHeader + 1), ExtHeader->eh_entries);
if (EFI_ERROR (Status) && (Status != EFI_OUT_OF_RESOURCES)) {
if (Buffer != NULL) {
FreePool (Buffer);
}
return Status;
}
Ext = Ext4BinsearchExtentExt (ExtHeader, LogicalBlock);
if (!Ext) {
if (Buffer != NULL) {
FreePool (Buffer);
}
return EFI_NO_MAPPING;
}
if (!((LogicalBlock >= Ext->ee_block) && (Ext->ee_block + Ext4GetExtentLength (Ext) > LogicalBlock))) {
// This extent does not cover the block
if (Buffer != NULL) {
FreePool (Buffer);
}
return EFI_NO_MAPPING;
}
*Extent = *Ext;
if (Buffer != NULL) {
FreePool (Buffer);
}
return EFI_SUCCESS;
}
/**
Compare two EXT4_EXTENT structs.
Used in the extent map's ORDERED_COLLECTION.
@param[in] UserStruct1 Pointer to the first user structure.
@param[in] UserStruct2 Pointer to the second user structure.
@retval <0 If UserStruct1 compares less than UserStruct2.
@retval 0 If UserStruct1 compares equal to UserStruct2.
@retval >0 If UserStruct1 compares greater than UserStruct2.
**/
STATIC
INTN
EFIAPI
Ext4ExtentsMapStructCompare (
IN CONST VOID *UserStruct1,
IN CONST VOID *UserStruct2
)
{
CONST EXT4_EXTENT *Extent1;
CONST EXT4_EXTENT *Extent2;
Extent1 = UserStruct1;
Extent2 = UserStruct2;
return Extent1->ee_block < Extent2->ee_block ? -1 :
Extent1->ee_block > Extent2->ee_block ? 1 : 0;
}
/**
Compare a standalone key against a EXT4_EXTENT containing an embedded key.
Used in the extent map's ORDERED_COLLECTION.
@param[in] StandaloneKey Pointer to the bare key.
@param[in] UserStruct Pointer to the user structure with the embedded
key.
@retval <0 If StandaloneKey compares less than UserStruct's key.
@retval 0 If StandaloneKey compares equal to UserStruct's key.
@retval >0 If StandaloneKey compares greater than UserStruct's key.
**/
STATIC
INTN
EFIAPI
Ext4ExtentsMapKeyCompare (
IN CONST VOID *StandaloneKey,
IN CONST VOID *UserStruct
)
{
CONST EXT4_EXTENT *Extent;
UINT32 Block;
// Note that logical blocks are 32-bits in size so no truncation can happen here
// with regards to 32-bit architectures.
Extent = UserStruct;
Block = (UINT32)(UINTN)StandaloneKey;
if ((Block >= Extent->ee_block) && (Block - Extent->ee_block < Ext4GetExtentLength (Extent))) {
return 0;
}
return Block < Extent->ee_block ? -1 :
Block > Extent->ee_block ? 1 : 0;
}
/**
Initialises the (empty) extents map, that will work as a cache of extents.
@param[in] File Pointer to the open file.
@return Result of the operation.
**/
EFI_STATUS
Ext4InitExtentsMap (
IN EXT4_FILE *File
)
{
File->ExtentsMap = OrderedCollectionInit (Ext4ExtentsMapStructCompare, Ext4ExtentsMapKeyCompare);
if (!File->ExtentsMap) {
return EFI_OUT_OF_RESOURCES;
}
return EFI_SUCCESS;
}
/**
Frees the extents map, deleting every extent stored.
@param[in] File Pointer to the open file.
**/
VOID
Ext4FreeExtentsMap (
IN EXT4_FILE *File
)
{
// Keep calling Min(), so we get an arbitrary node we can delete.
// If Min() returns NULL, it's empty.
ORDERED_COLLECTION_ENTRY *MinEntry;
EXT4_EXTENT *Ext;
MinEntry = NULL;
while ((MinEntry = OrderedCollectionMin (File->ExtentsMap)) != NULL) {
OrderedCollectionDelete (File->ExtentsMap, MinEntry, (VOID **)&Ext);
FreePool (Ext);
}
ASSERT (OrderedCollectionIsEmpty (File->ExtentsMap));
OrderedCollectionUninit (File->ExtentsMap);
File->ExtentsMap = NULL;
}
/**
Caches a range of extents, by allocating pool memory for each extent and adding it to the tree.
@param[in] File Pointer to the open file.
@param[in] Extents Pointer to an array of extents.
@param[in] NumberExtents Length of the array.
@return Result of the caching
**/
STATIC
EFI_STATUS
Ext4CacheExtents (
IN EXT4_FILE *File,
IN CONST EXT4_EXTENT *Extents,
IN UINT16 NumberExtents
)
{
UINT16 Idx;
EXT4_EXTENT *Extent;
EFI_STATUS Status;
/* Note that any out of memory condition might mean we don't get to cache a whole leaf of extents
* in which case, future insertions might fail.
*/
for (Idx = 0; Idx < NumberExtents; Idx++, Extents++) {
if (Extents->ee_len == 0) {
// 0-sized extent, must be corruption
return EFI_VOLUME_CORRUPTED;
}
Extent = AllocatePool (sizeof (EXT4_EXTENT));
if (Extent == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (Extent, Extents, sizeof (EXT4_EXTENT));
Status = OrderedCollectionInsert (File->ExtentsMap, NULL, Extent);
// EFI_ALREADY_STARTED = already exists in the tree.
if (EFI_ERROR (Status)) {
FreePool (Extent);
if (Status == EFI_ALREADY_STARTED) {
continue;
}
return EFI_SUCCESS;
}
}
return EFI_SUCCESS;
}
/**
Gets an extent from the extents cache of the file.
@param[in] File Pointer to the open file.
@param[in] Block Block we want to grab.
@return Pointer to the extent, or NULL if it was not found.
**/
EXT4_EXTENT *
Ext4GetExtentFromMap (
IN EXT4_FILE *File,
IN UINT32 Block
)
{
ORDERED_COLLECTION_ENTRY *Entry;
Entry = OrderedCollectionFind (File->ExtentsMap, (CONST VOID *)(UINTN)Block);
if (Entry == NULL) {
return NULL;
}
return OrderedCollectionUserStruct (Entry);
}
/**
Calculates the checksum of the extent data block.
@param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER.
@param[in] File Pointer to the file.
@return The checksum.
**/
UINT32
Ext4CalculateExtentChecksum (
IN CONST EXT4_EXTENT_HEADER *ExtHeader,
IN CONST EXT4_FILE *File
)
{
UINT32 Csum;
EXT4_PARTITION *Partition;
EXT4_INODE *Inode;
Partition = File->Partition;
Inode = File->Inode;
Csum = Ext4CalculateChecksum (Partition, &File->InodeNum, sizeof (EXT4_INO_NR), Partition->InitialSeed);
Csum = Ext4CalculateChecksum (Partition, &Inode->i_generation, sizeof (Inode->i_generation), Csum);
Csum = Ext4CalculateChecksum (Partition, ExtHeader, Partition->BlockSize - sizeof (EXT4_EXTENT_TAIL), Csum);
return Csum;
}
/**
Checks if the checksum of the extent data block is correct.
@param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER.
@param[in] File Pointer to the file.
@return TRUE if the checksum is correct, FALSE if there is corruption.
**/
BOOLEAN
Ext4CheckExtentChecksum (
IN CONST EXT4_EXTENT_HEADER *ExtHeader,
IN CONST EXT4_FILE *File
)
{
EXT4_PARTITION *Partition;
EXT4_EXTENT_TAIL *Tail;
Partition = File->Partition;
if (!EXT4_HAS_METADATA_CSUM (Partition)) {
return TRUE;
}
Tail = (EXT4_EXTENT_TAIL *)((CONST CHAR8 *)ExtHeader + (Partition->BlockSize - 4));
return Tail->eb_checksum == Ext4CalculateExtentChecksum (ExtHeader, File);
}
/**
Retrieves the extent's length, dealing with uninitialized extents in the process.
@param[in] Extent Pointer to the EXT4_EXTENT
@returns Extent's length, in filesystem blocks.
**/
EXT4_BLOCK_NR
Ext4GetExtentLength (
IN CONST EXT4_EXTENT *Extent
)
{
// If it's an uninitialized extent, the true length is ee_len - 2^15
if (EXT4_EXTENT_IS_UNINITIALIZED (Extent)) {
return Extent->ee_len - EXT4_EXTENT_MAX_INITIALIZED;
}
return Extent->ee_len;
}

1012
Ext4Pkg/Ext4Dxe/File.c Normal file

File diff suppressed because it is too large Load Diff

490
Ext4Pkg/Ext4Dxe/Inode.c Normal file
View File

@ -0,0 +1,490 @@
/** @file
Inode related routines
Copyright (c) 2021 - 2022 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
EpochToEfiTime copied from EmbeddedPkg/Library/TimeBaseLib.c
Copyright (c) 2016, Hisilicon Limited. All rights reserved.
Copyright (c) 2016-2019, Linaro Limited. All rights reserved.
Copyright (c) 2021, Ampere Computing LLC. All rights reserved.
**/
#include "Ext4Dxe.h"
/**
Calculates the checksum of the given inode.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] Inode Pointer to the inode.
@param[in] InodeNum Inode number.
@return The checksum.
**/
UINT32
Ext4CalculateInodeChecksum (
IN CONST EXT4_PARTITION *Partition,
IN CONST EXT4_INODE *Inode,
IN EXT4_INO_NR InodeNum
)
{
UINT32 Crc;
UINT16 Dummy;
BOOLEAN HasSecondChecksumField;
CONST VOID *RestOfInode;
UINTN RestOfInodeLength;
UINTN Length;
HasSecondChecksumField = EXT4_INODE_HAS_FIELD (Inode, i_checksum_hi);
Dummy = 0;
Crc = Ext4CalculateChecksum (Partition, &InodeNum, sizeof (InodeNum), Partition->InitialSeed);
Crc = Ext4CalculateChecksum (Partition, &Inode->i_generation, sizeof (Inode->i_generation), Crc);
Crc = Ext4CalculateChecksum (
Partition,
Inode,
OFFSET_OF (EXT4_INODE, i_osd2.data_linux.l_i_checksum_lo),
Crc
);
Crc = Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), Crc);
RestOfInode = &Inode->i_osd2.data_linux.l_i_reserved;
RestOfInodeLength = Partition->InodeSize - OFFSET_OF (EXT4_INODE, i_osd2.data_linux.l_i_reserved);
if (HasSecondChecksumField) {
Length = OFFSET_OF (EXT4_INODE, i_checksum_hi) - OFFSET_OF (EXT4_INODE, i_osd2.data_linux.l_i_reserved);
Crc = Ext4CalculateChecksum (Partition, &Inode->i_osd2.data_linux.l_i_reserved, Length, Crc);
Crc = Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), Crc);
// 4 is the size of the i_extra_size field + the size of i_checksum_hi
RestOfInodeLength = Partition->InodeSize - EXT4_GOOD_OLD_INODE_SIZE - 4;
RestOfInode = &Inode->i_ctime_extra;
}
Crc = Ext4CalculateChecksum (Partition, RestOfInode, RestOfInodeLength, Crc);
return Crc;
}
/**
Reads from an EXT4 inode.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] File Pointer to the opened file.
@param[out] Buffer Pointer to the buffer.
@param[in] Offset Offset of the read.
@param[in out] Length Pointer to the length of the buffer, in bytes.
After a successful read, it's updated to the number of read bytes.
@return Status of the read operation.
**/
EFI_STATUS
Ext4Read (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT VOID *Buffer,
IN UINT64 Offset,
IN OUT UINTN *Length
)
{
EXT4_INODE *Inode;
UINT64 InodeSize;
UINT64 CurrentSeek;
UINTN RemainingRead;
UINTN BeenRead;
UINTN WasRead;
EXT4_EXTENT Extent;
UINT32 BlockOff;
EFI_STATUS Status;
BOOLEAN HasBackingExtent;
UINT32 HoleOff;
UINT64 HoleLen;
UINT64 ExtentStartBytes;
UINT64 ExtentLengthBytes;
UINT64 ExtentLogicalBytes;
// Our extent offset is the difference between CurrentSeek and ExtentLogicalBytes
UINT64 ExtentOffset;
UINTN ExtentMayRead;
Inode = File->Inode;
InodeSize = EXT4_INODE_SIZE (Inode);
CurrentSeek = Offset;
RemainingRead = *Length;
BeenRead = 0;
DEBUG ((DEBUG_FS, "[ext4] Ext4Read(%s, Offset %lu, Length %lu)\n", File->Dentry->Name, Offset, *Length));
if (Offset > InodeSize) {
return EFI_DEVICE_ERROR;
}
if (RemainingRead > InodeSize - Offset) {
RemainingRead = (UINTN)(InodeSize - Offset);
}
while (RemainingRead != 0) {
WasRead = 0;
// The algorithm here is to get the extent corresponding to the current block
// and then read as much as we can from the current extent.
Status = Ext4GetExtent (
Partition,
File,
DivU64x32Remainder (CurrentSeek, Partition->BlockSize, &BlockOff),
&Extent
);
if ((Status != EFI_SUCCESS) && (Status != EFI_NO_MAPPING)) {
return Status;
}
HasBackingExtent = Status != EFI_NO_MAPPING;
if (!HasBackingExtent || EXT4_EXTENT_IS_UNINITIALIZED (&Extent)) {
HoleOff = BlockOff;
if (!HasBackingExtent) {
HoleLen = Partition->BlockSize - HoleOff;
} else {
// Uninitialized extents behave exactly the same as file holes, except they have
// blocks already allocated to them.
HoleLen = MultU64x32 (Ext4GetExtentLength (&Extent), Partition->BlockSize) - HoleOff;
}
WasRead = HoleLen > RemainingRead ? RemainingRead : (UINTN)HoleLen;
// Potential improvement: In the future, we could get the file hole's total
// size and memset all that
ZeroMem (Buffer, WasRead);
} else {
ExtentStartBytes = MultU64x32 (
LShiftU64 (Extent.ee_start_hi, 32) |
Extent.ee_start_lo,
Partition->BlockSize
);
ExtentLengthBytes = Extent.ee_len * Partition->BlockSize;
ExtentLogicalBytes = MultU64x32 ((UINT64)Extent.ee_block, Partition->BlockSize);
ExtentOffset = CurrentSeek - ExtentLogicalBytes;
ExtentMayRead = (UINTN)(ExtentLengthBytes - ExtentOffset);
WasRead = ExtentMayRead > RemainingRead ? RemainingRead : ExtentMayRead;
Status = Ext4ReadDiskIo (Partition, Buffer, WasRead, ExtentStartBytes + ExtentOffset);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"[ext4] Error %r reading [%lu, %lu]\n",
Status,
ExtentStartBytes + ExtentOffset,
ExtentStartBytes + ExtentOffset + WasRead - 1
));
return Status;
}
}
RemainingRead -= WasRead;
Buffer = (VOID *)((CHAR8 *)Buffer + WasRead);
BeenRead += WasRead;
CurrentSeek += WasRead;
}
*Length = BeenRead;
return EFI_SUCCESS;
}
/**
Allocates a zeroed inode structure.
@param[in] Partition Pointer to the opened EXT4 partition.
@return Pointer to the allocated structure, from the pool,
with size Partition->InodeSize.
**/
EXT4_INODE *
Ext4AllocateInode (
IN EXT4_PARTITION *Partition
)
{
BOOLEAN NeedsToZeroRest;
UINT32 InodeSize;
EXT4_INODE *Inode;
NeedsToZeroRest = FALSE;
InodeSize = Partition->InodeSize;
// We allocate a structure of at least sizeof(EXT4_INODE), but in the future, when
// write support is added and we need to flush inodes to disk, we could have a bit better
// distinction between the on-disk inode and a separate, nicer to work with inode struct.
// It's important to note that EXT4_INODE includes fields that may not exist in an actual
// filesystem (the minimum inode size is 128 byte and at the moment the size of EXT4_INODE
// is 160 bytes).
if (InodeSize < sizeof (EXT4_INODE)) {
InodeSize = sizeof (EXT4_INODE);
NeedsToZeroRest = TRUE;
}
Inode = AllocateZeroPool (InodeSize);
if (Inode == NULL) {
return NULL;
}
if (NeedsToZeroRest) {
Inode->i_extra_isize = 0;
}
return Inode;
}
/**
Checks if a file is a directory.
@param[in] File Pointer to the opened file.
@return TRUE if file is a directory.
**/
BOOLEAN
Ext4FileIsDir (
IN CONST EXT4_FILE *File
)
{
return (File->Inode->i_mode & EXT4_INO_TYPE_DIR) == EXT4_INO_TYPE_DIR;
}
/**
Checks if a file is a symlink.
@param[in] File Pointer to the opened file.
@return BOOLEAN Whether file is a symlink
**/
BOOLEAN
Ext4FileIsSymlink (
IN CONST EXT4_FILE *File
)
{
return (File->Inode->i_mode & EXT4_INO_TYPE_SYMLINK) == EXT4_INO_TYPE_SYMLINK;
}
/**
Checks if a file is a regular file.
@param[in] File Pointer to the opened file.
@return BOOLEAN TRUE if file is a regular file.
**/
BOOLEAN
Ext4FileIsReg (
IN CONST EXT4_FILE *File
)
{
return (File->Inode->i_mode & EXT4_INO_TYPE_REGFILE) == EXT4_INO_TYPE_REGFILE;
}
/**
Calculates the physical space used by a file.
@param[in] File Pointer to the opened file.
@return Physical space used by a file, in bytes.
**/
UINT64
Ext4FilePhysicalSpace (
IN EXT4_FILE *File
)
{
BOOLEAN HugeFile;
UINT64 Blocks;
HugeFile = EXT4_HAS_RO_COMPAT (File->Partition, EXT4_FEATURE_RO_COMPAT_HUGE_FILE);
Blocks = File->Inode->i_blocks;
if (HugeFile) {
Blocks |= LShiftU64 (File->Inode->i_osd2.data_linux.l_i_blocks_high, 32);
// If HUGE_FILE is enabled and EXT4_HUGE_FILE_FL is set in the inode's flags, each unit
// in i_blocks corresponds to an actual filesystem block
if ((File->Inode->i_flags & EXT4_HUGE_FILE_FL) != 0) {
return MultU64x32 (Blocks, File->Partition->BlockSize);
}
}
// Else, each i_blocks unit corresponds to 512 bytes
return MultU64x32 (Blocks, 512);
}
// Copied from EmbeddedPkg at my mentor's request.
// The lack of comments and good variable names is frightening...
/**
Converts Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC) to EFI_TIME.
@param[in] EpochSeconds Epoch seconds.
@param[out] Time The time converted to UEFI format.
**/
STATIC
VOID
EFIAPI
EpochToEfiTime (
IN UINTN EpochSeconds,
OUT EFI_TIME *Time
)
{
UINTN a;
UINTN b;
UINTN c;
UINTN d;
UINTN g;
UINTN j;
UINTN m;
UINTN y;
UINTN da;
UINTN db;
UINTN dc;
UINTN dg;
UINTN hh;
UINTN mm;
UINTN ss;
UINTN J;
J = (EpochSeconds / 86400) + 2440588;
j = J + 32044;
g = j / 146097;
dg = j % 146097;
c = (((dg / 36524) + 1) * 3) / 4;
dc = dg - (c * 36524);
b = dc / 1461;
db = dc % 1461;
a = (((db / 365) + 1) * 3) / 4;
da = db - (a * 365);
y = (g * 400) + (c * 100) + (b * 4) + a;
m = (((da * 5) + 308) / 153) - 2;
d = da - (((m + 4) * 153) / 5) + 122;
Time->Year = (UINT16)(y - 4800 + ((m + 2) / 12));
Time->Month = ((m + 2) % 12) + 1;
Time->Day = (UINT8)(d + 1);
ss = EpochSeconds % 60;
a = (EpochSeconds - ss) / 60;
mm = a % 60;
b = (a - mm) / 60;
hh = b % 24;
Time->Hour = (UINT8)hh;
Time->Minute = (UINT8)mm;
Time->Second = (UINT8)ss;
Time->Nanosecond = 0;
}
// The time format used to (de/en)code timestamp and timestamp_extra is documented on
// the ext4 docs page in kernel.org
#define EXT4_EXTRA_TIMESTAMP_MASK ((1 << 2) - 1)
#define EXT4_FILE_GET_TIME_GENERIC(Name, Field) \
VOID \
Ext4File ## Name (IN EXT4_FILE *File, OUT EFI_TIME *Time) \
{ \
EXT4_INODE *Inode = File->Inode; \
UINT64 SecondsEpoch = Inode->Field; \
UINT32 Nanoseconds = 0; \
\
if (EXT4_INODE_HAS_FIELD (Inode, Field ## _extra)) { \
SecondsEpoch |= LShiftU64 ((UINT64)(Inode->Field ## _extra & EXT4_EXTRA_TIMESTAMP_MASK), 32); \
Nanoseconds = Inode->Field ## _extra >> 2; \
} \
EpochToEfiTime ((UINTN)SecondsEpoch, Time); \
Time->Nanosecond = Nanoseconds; \
}
// Note: EpochToEfiTime should be adjusted to take in a UINT64 instead of a UINTN, in order to avoid Y2038
// on 32-bit systems.
/**
Gets the file's last access time.
@param[in] File Pointer to the opened file.
@param[out] Time Pointer to an EFI_TIME structure.
**/
EXT4_FILE_GET_TIME_GENERIC (ATime, i_atime);
/**
Gets the file's last (data) modification time.
@param[in] File Pointer to the opened file.
@param[out] Time Pointer to an EFI_TIME structure.
**/
EXT4_FILE_GET_TIME_GENERIC (MTime, i_mtime);
/**
Gets the file's creation time.
@param[in] File Pointer to the opened file.
@param[out] Time Pointer to an EFI_TIME structure.
**/
STATIC
EXT4_FILE_GET_TIME_GENERIC (
CrTime,
i_crtime
);
/**
Gets the file's creation time, if possible.
@param[in] File Pointer to the opened file.
@param[out] Time Pointer to an EFI_TIME structure.
In the case where the the creation time isn't recorded,
Time is zeroed.
**/
VOID
Ext4FileCreateTime (
IN EXT4_FILE *File,
OUT EFI_TIME *Time
)
{
EXT4_INODE *Inode;
Inode = File->Inode;
if (!EXT4_INODE_HAS_FIELD (Inode, i_crtime)) {
ZeroMem (Time, sizeof (EFI_TIME));
return;
}
Ext4FileCrTime (File, Time);
}
/**
Checks if the checksum of the inode is correct.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] Inode Pointer to the inode.
@param[in] InodeNum Inode number.
@return TRUE if checksum is correct, FALSE if there is corruption.
**/
BOOLEAN
Ext4CheckInodeChecksum (
IN CONST EXT4_PARTITION *Partition,
IN CONST EXT4_INODE *Inode,
IN EXT4_INO_NR InodeNum
)
{
UINT32 Csum;
UINT32 DiskCsum;
if (!EXT4_HAS_METADATA_CSUM (Partition)) {
return TRUE;
}
Csum = Ext4CalculateInodeChecksum (Partition, Inode, InodeNum);
DiskCsum = Inode->i_osd2.data_linux.l_i_checksum_lo;
if (EXT4_INODE_HAS_FIELD (Inode, i_checksum_hi)) {
DiskCsum |= ((UINT32)Inode->i_checksum_hi) << 16;
} else {
// Only keep the lower bits for the comparison if the checksum is 16 bits.
Csum &= 0xffff;
}
return Csum == DiskCsum;
}

132
Ext4Pkg/Ext4Dxe/Partition.c Normal file
View File

@ -0,0 +1,132 @@
/** @file
Driver entry point
Copyright (c) 2021 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
/**
Opens an ext4 partition and installs the Simple File System protocol.
@param[in] DeviceHandle Handle to the block device.
@param[in] DiskIo Pointer to an EFI_DISK_IO_PROTOCOL.
@param[in opt] DiskIo2 Pointer to an EFI_DISK_IO2_PROTOCOL, if supported.
@param[in] BlockIo Pointer to an EFI_BLOCK_IO_PROTOCOL.
@retval EFI_SUCCESS The opening was successful.
!EFI_SUCCESS Opening failed.
**/
EFI_STATUS
Ext4OpenPartition (
IN EFI_HANDLE DeviceHandle,
IN EFI_DISK_IO_PROTOCOL *DiskIo,
IN OPTIONAL EFI_DISK_IO2_PROTOCOL *DiskIo2,
IN EFI_BLOCK_IO_PROTOCOL *BlockIo
)
{
EXT4_PARTITION *Part;
EFI_STATUS Status;
Part = AllocateZeroPool (sizeof (*Part));
if (Part == NULL) {
return EFI_OUT_OF_RESOURCES;
}
InitializeListHead (&Part->OpenFiles);
Part->BlockIo = BlockIo;
Part->DiskIo = DiskIo;
Part->DiskIo2 = DiskIo2;
Status = Ext4OpenSuperblock (Part);
if (EFI_ERROR (Status)) {
FreePool (Part);
return Status;
}
Part->Interface.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
Part->Interface.OpenVolume = Ext4OpenVolume;
Status = gBS->InstallMultipleProtocolInterfaces (
&DeviceHandle,
&gEfiSimpleFileSystemProtocolGuid,
&Part->Interface,
NULL
);
if (EFI_ERROR (Status)) {
FreePool (Part);
return Status;
}
return EFI_SUCCESS;
}
/**
Sets up the protocol and metadata of a file that is being opened.
@param[in out] File Pointer to the file.
@param[in] Partition Pointer to the opened partition.
**/
VOID
Ext4SetupFile (
IN OUT EXT4_FILE *File,
IN EXT4_PARTITION *Partition
)
{
// Note: We don't yet support revision 2 of the file protocol
// (needs DISK_IO2 + asynchronous IO)
File->Protocol.Revision = EFI_FILE_PROTOCOL_REVISION;
File->Protocol.Open = Ext4Open;
File->Protocol.Close = Ext4Close;
File->Protocol.Delete = Ext4Delete;
File->Protocol.Read = Ext4ReadFile;
File->Protocol.Write = Ext4WriteFile;
File->Protocol.SetPosition = Ext4SetPosition;
File->Protocol.GetPosition = Ext4GetPosition;
File->Protocol.GetInfo = Ext4GetInfo;
File->Protocol.SetInfo = Ext4SetInfo;
File->Partition = Partition;
}
/**
Unmounts and frees an ext4 partition.
@param[in] Partition Pointer to the opened partition.
@retval Status of the unmount.
**/
EFI_STATUS
Ext4UnmountAndFreePartition (
IN EXT4_PARTITION *Partition
)
{
LIST_ENTRY *Entry;
LIST_ENTRY *NextEntry;
EXT4_FILE *File;
BOOLEAN DeletedRootDentry;
Partition->Unmounting = TRUE;
Ext4CloseInternal (Partition->Root);
BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Partition->OpenFiles) {
File = EXT4_FILE_FROM_OPEN_FILES_NODE (Entry);
Ext4CloseInternal (File);
}
DeletedRootDentry = Ext4UnrefDentry (Partition->RootDentry);
if (!DeletedRootDentry) {
DEBUG ((DEBUG_ERROR, "[ext4] Failed to delete root dentry - resource leak present.\n"));
}
FreePool (Partition->BlockGroups);
FreePool (Partition);
return EFI_SUCCESS;
}

View File

@ -0,0 +1,363 @@
/** @file
Superblock managing routines
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
STATIC CONST UINT32 gSupportedCompatFeat = EXT4_FEATURE_COMPAT_EXT_ATTR;
STATIC CONST UINT32 gSupportedRoCompatFeat =
EXT4_FEATURE_RO_COMPAT_DIR_NLINK | EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE |
EXT4_FEATURE_RO_COMPAT_HUGE_FILE | EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
EXT4_FEATURE_RO_COMPAT_GDT_CSUM | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM | EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER;
STATIC CONST UINT32 gSupportedIncompatFeat =
EXT4_FEATURE_INCOMPAT_64BIT | EXT4_FEATURE_INCOMPAT_DIRDATA |
EXT4_FEATURE_INCOMPAT_FLEX_BG | EXT4_FEATURE_INCOMPAT_FILETYPE |
EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_LARGEDIR |
EXT4_FEATURE_INCOMPAT_MMP | EXT4_FEATURE_INCOMPAT_RECOVER | EXT4_FEATURE_INCOMPAT_CSUM_SEED;
// Future features that may be nice additions in the future:
// 1) Btree support: Required for write support and would speed up lookups in large directories.
// 2) meta_bg: Required to mount meta_bg-enabled partitions.
// Note: We ignore MMP because it's impossible that it's mapped elsewhere,
// I think (unless there's some sort of network setup where we're accessing a remote partition).
// Note on corruption signaling:
// We (Ext4Dxe) could signal corruption by setting s_state to |= EXT4_FS_STATE_ERRORS_DETECTED.
// I've decided against that, because right now the driver is read-only, and
// that would mean we would need to writeback the superblock. If something like
// this is desired, it's fairly trivial to look for EFI_VOLUME_CORRUPTED
// references and add some Ext4SignalCorruption function + function call.
/**
Checks the superblock's magic value.
@param[in] DiskIo Pointer to the DiskIo.
@param[in] BlockIo Pointer to the BlockIo.
@returns Whether the partition has a valid EXT4 superblock magic value.
**/
BOOLEAN
Ext4SuperblockCheckMagic (
IN EFI_DISK_IO_PROTOCOL *DiskIo,
IN EFI_BLOCK_IO_PROTOCOL *BlockIo
)
{
UINT16 Magic;
EFI_STATUS Status;
Status = DiskIo->ReadDisk (
DiskIo,
BlockIo->Media->MediaId,
EXT4_SUPERBLOCK_OFFSET + OFFSET_OF (EXT4_SUPERBLOCK, s_magic),
sizeof (Magic),
&Magic
);
if (EFI_ERROR (Status)) {
return FALSE;
}
if (Magic != EXT4_SIGNATURE) {
return FALSE;
}
return TRUE;
}
/**
Does brief validation of the ext4 superblock.
@param[in] Sb Pointer to the read superblock.
@return TRUE if a valid ext4 superblock, else FALSE.
**/
BOOLEAN
Ext4SuperblockValidate (
CONST EXT4_SUPERBLOCK *Sb
)
{
if (Sb->s_magic != EXT4_SIGNATURE) {
return FALSE;
}
if ((Sb->s_rev_level != EXT4_DYNAMIC_REV) && (Sb->s_rev_level != EXT4_GOOD_OLD_REV)) {
return FALSE;
}
if ((Sb->s_state & EXT4_FS_STATE_UNMOUNTED) == 0) {
DEBUG ((DEBUG_WARN, "[ext4] Filesystem was not unmounted cleanly\n"));
}
return TRUE;
}
/**
Calculates the superblock's checksum.
@param[in] Partition Pointer to the opened partition.
@param[in] Sb Pointer to the superblock.
@return The superblock's checksum.
**/
STATIC
UINT32
Ext4CalculateSuperblockChecksum (
EXT4_PARTITION *Partition,
CONST EXT4_SUPERBLOCK *Sb
)
{
// Most checksums require us to go through a dummy 0 as part of the requirement
// that the checksum is done over a structure with its checksum field = 0.
UINT32 Checksum;
Checksum = Ext4CalculateChecksum (
Partition,
Sb,
OFFSET_OF (EXT4_SUPERBLOCK, s_checksum),
~0U
);
return Checksum;
}
/**
Verifies that the superblock's checksum is valid.
@param[in] Partition Pointer to the opened partition.
@param[in] Sb Pointer to the superblock.
@return The superblock's checksum.
**/
STATIC
BOOLEAN
Ext4VerifySuperblockChecksum (
EXT4_PARTITION *Partition,
CONST EXT4_SUPERBLOCK *Sb
)
{
if (!EXT4_HAS_METADATA_CSUM (Partition)) {
return TRUE;
}
return Sb->s_checksum == Ext4CalculateSuperblockChecksum (Partition, Sb);
}
/**
Opens and parses the superblock.
@param[out] Partition Partition structure to fill with filesystem details.
@retval EFI_SUCCESS Parsing was successful and the partition is a
valid ext4 partition.
**/
EFI_STATUS
Ext4OpenSuperblock (
OUT EXT4_PARTITION *Partition
)
{
UINT32 Index;
EFI_STATUS Status;
EXT4_SUPERBLOCK *Sb;
UINT32 NrBlocksRem;
UINTN NrBlocks;
UINT32 UnsupportedRoCompat;
EXT4_BLOCK_GROUP_DESC *Desc;
Status = Ext4ReadDiskIo (
Partition,
&Partition->SuperBlock,
sizeof (EXT4_SUPERBLOCK),
EXT4_SUPERBLOCK_OFFSET
);
if (EFI_ERROR (Status)) {
return Status;
}
Sb = &Partition->SuperBlock;
if (!Ext4SuperblockValidate (Sb)) {
return EFI_VOLUME_CORRUPTED;
}
if (Sb->s_rev_level == EXT4_DYNAMIC_REV) {
Partition->FeaturesCompat = Sb->s_feature_compat;
Partition->FeaturesIncompat = Sb->s_feature_incompat;
Partition->FeaturesRoCompat = Sb->s_feature_ro_compat;
Partition->InodeSize = Sb->s_inode_size;
// Check for proper alignment of InodeSize and that InodeSize is indeed larger than
// the minimum size, 128 bytes.
if (((Partition->InodeSize % 4) != 0) || (Partition->InodeSize < EXT4_GOOD_OLD_INODE_SIZE)) {
return EFI_VOLUME_CORRUPTED;
}
} else {
// GOOD_OLD_REV
Partition->FeaturesCompat = Partition->FeaturesIncompat = Partition->FeaturesRoCompat = 0;
Partition->InodeSize = EXT4_GOOD_OLD_INODE_SIZE;
}
// Now, check for the feature set of the filesystem
// It's essential to check for this to avoid filesystem corruption and to avoid
// accidentally opening an ext2/3/4 filesystem we don't understand, which would be disastrous.
if (Partition->FeaturesIncompat & ~gSupportedIncompatFeat) {
DEBUG ((
DEBUG_ERROR,
"[ext4] Unsupported features %lx\n",
Partition->FeaturesIncompat & ~gSupportedIncompatFeat
));
return EFI_UNSUPPORTED;
}
if (EXT4_HAS_INCOMPAT (Partition, EXT4_FEATURE_INCOMPAT_RECOVER)) {
DEBUG ((DEBUG_WARN, "[ext4] Needs journal recovery, mounting read-only\n"));
Partition->ReadOnly = TRUE;
}
// At the time of writing, it's the only supported checksum.
if (EXT4_HAS_METADATA_CSUM (Partition) && (Sb->s_checksum_type != EXT4_CHECKSUM_CRC32C)) {
return EFI_UNSUPPORTED;
}
if (EXT4_HAS_INCOMPAT (Partition, EXT4_FEATURE_INCOMPAT_CSUM_SEED)) {
Partition->InitialSeed = Sb->s_checksum_seed;
} else {
Partition->InitialSeed = Ext4CalculateChecksum (Partition, Sb->s_uuid, 16, ~0U);
}
UnsupportedRoCompat = Partition->FeaturesRoCompat & ~gSupportedRoCompatFeat;
if (UnsupportedRoCompat != 0) {
DEBUG ((DEBUG_WARN, "[ext4] Unsupported ro compat %x\n", UnsupportedRoCompat));
Partition->ReadOnly = TRUE;
}
// gSupportedCompatFeat is documentation-only since we never need to access it.
// The line below avoids unused variable warnings.
(VOID)gSupportedCompatFeat;
DEBUG ((DEBUG_FS, "Read only = %u\n", Partition->ReadOnly));
if (Sb->s_inodes_per_group == 0) {
DEBUG ((DEBUG_ERROR, "[ext4] Inodes per group can not be zero\n"));
return EFI_VOLUME_CORRUPTED;
}
if (Sb->s_log_block_size > EXT4_LOG_BLOCK_SIZE_MAX) {
DEBUG ((DEBUG_ERROR, "[ext4] SuperBlock s_log_block_size %lu is too big\n", Sb->s_log_block_size));
return EFI_UNSUPPORTED;
}
Partition->BlockSize = (UINT32)LShiftU64 (1024, Sb->s_log_block_size);
// The size of a block group can also be calculated as 8 * Partition->BlockSize
if (Sb->s_blocks_per_group != 8 * Partition->BlockSize) {
return EFI_UNSUPPORTED;
}
Partition->NumberBlocks = EXT4_BLOCK_NR_FROM_HALFS (Partition, Sb->s_blocks_count, Sb->s_blocks_count_hi);
Partition->NumberBlockGroups = DivU64x32 (Partition->NumberBlocks, Sb->s_blocks_per_group);
DEBUG ((
DEBUG_FS,
"[ext4] Number of blocks = %lu\n[ext4] Number of block groups: %lu\n",
Partition->NumberBlocks,
Partition->NumberBlockGroups
));
if (EXT4_IS_64_BIT (Partition)) {
// s_desc_size should be 4 byte aligned and
// 64 bit filesystems need DescSize to be 64 bytes
if (((Sb->s_desc_size % 4) != 0) || (Sb->s_desc_size < EXT4_64BIT_BLOCK_DESC_SIZE)) {
return EFI_VOLUME_CORRUPTED;
}
Partition->DescSize = Sb->s_desc_size;
} else {
Partition->DescSize = EXT4_OLD_BLOCK_DESC_SIZE;
}
if (!Ext4VerifySuperblockChecksum (Partition, Sb)) {
DEBUG ((DEBUG_ERROR, "[ext4] Bad superblock checksum %lx\n", Ext4CalculateSuperblockChecksum (Partition, Sb)));
return EFI_VOLUME_CORRUPTED;
}
NrBlocks = (UINTN)DivU64x32Remainder (
MultU64x32 (Partition->NumberBlockGroups, Partition->DescSize),
Partition->BlockSize,
&NrBlocksRem
);
if (NrBlocksRem != 0) {
NrBlocks++;
}
Partition->BlockGroups = Ext4AllocAndReadBlocks (Partition, NrBlocks, Partition->BlockSize == 1024 ? 2 : 1);
if (Partition->BlockGroups == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < Partition->NumberBlockGroups; Index++) {
Desc = Ext4GetBlockGroupDesc (Partition, Index);
if (!Ext4VerifyBlockGroupDescChecksum (Partition, Desc, Index)) {
DEBUG ((DEBUG_ERROR, "[ext4] Block group descriptor %u has an invalid checksum\n", Index));
FreePool (Partition->BlockGroups);
return EFI_VOLUME_CORRUPTED;
}
}
// RootDentry will serve as the basis of our directory entry tree.
Partition->RootDentry = Ext4CreateDentry (L"\\", NULL);
if (Partition->RootDentry == NULL) {
FreePool (Partition->BlockGroups);
return EFI_OUT_OF_RESOURCES;
}
// Note that the cast below is completely safe, because EXT4_FILE is a specialization of EFI_FILE_PROTOCOL
Status = Ext4OpenVolume (&Partition->Interface, (EFI_FILE_PROTOCOL **)&Partition->Root);
if (EFI_ERROR (Status)) {
Ext4UnrefDentry (Partition->RootDentry);
FreePool (Partition->BlockGroups);
}
return Status;
}
/**
Calculates the checksum of the given buffer.
@param[in] Partition Pointer to the opened EXT4 partition.
@param[in] Buffer Pointer to the buffer.
@param[in] Length Length of the buffer, in bytes.
@param[in] InitialValue Initial value of the CRC.
@return The checksum.
**/
UINT32
Ext4CalculateChecksum (
IN CONST EXT4_PARTITION *Partition,
IN CONST VOID *Buffer,
IN UINTN Length,
IN UINT32 InitialValue
)
{
if (!EXT4_HAS_METADATA_CSUM (Partition)) {
return 0;
}
switch (Partition->SuperBlock.s_checksum_type) {
case EXT4_CHECKSUM_CRC32C:
// For some reason, EXT4 really likes non-inverted CRC32C checksums, so we stick to that here.
return ~CalculateCrc32c(Buffer, Length, ~InitialValue);
default:
ASSERT (FALSE);
return 0;
}
}

261
Ext4Pkg/Ext4Dxe/Symlink.c Normal file
View File

@ -0,0 +1,261 @@
/** @file
Symbolic links routines
Copyright (c) 2022-2023 Savva Mitrofanov All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
/**
Detects if a symlink is a fast symlink.
@param[in] File Pointer to the opened file.
@return BOOLEAN Whether symlink is a fast symlink
**/
STATIC
BOOLEAN
Ext4SymlinkIsFastSymlink (
IN CONST EXT4_FILE *File
)
{
//
// Detection logic of the fast-symlink splits into two behaviors - old and new.
// The old behavior is based on comparing the extended attribute blocks
// with the inode's i_blocks, and if it's zero we know the inode isn't storing
// the link in filesystem blocks, so we look to the inode->i_data.
// The new behavior is apparently needed only with the large EA inode feature.
// In this case we check that inode size less than maximum fast symlink size.
// So, we revert to the old behavior if the large EA inode feature is not set.
//
UINT32 FileAcl;
UINT32 ExtAttrBlocks;
if ((File->Inode->i_flags & EXT4_EA_INODE_FL) == 0) {
FileAcl = File->Inode->i_file_acl;
if (EXT4_IS_64_BIT (File->Partition)) {
//
// We don't care about final value, we are just checking for any bit is set
// so, thats why we neglect LShiftU64(.., 32)
//
FileAcl |= File->Inode->i_osd2.data_linux.l_i_file_acl_high;
}
ExtAttrBlocks = FileAcl != 0 ? (File->Partition->BlockSize >> 9) : 0;
return File->Inode->i_blocks == ExtAttrBlocks;
}
return EXT4_INODE_SIZE (File->Inode) <= EXT4_FAST_SYMLINK_MAX_SIZE;
}
/**
Reads a fast symlink file.
@param[in] Partition Pointer to the ext4 partition.
@param[in] File Pointer to the open symlink file.
@param[out] AsciiSymlink Pointer to the output ascii symlink string.
@param[out] AsciiSymlinkSize Pointer to the output ascii symlink string length.
@retval EFI_SUCCESS Fast symlink was read.
@retval EFI_OUT_OF_RESOURCES Memory allocation error.
**/
STATIC
EFI_STATUS
Ext4ReadFastSymlink (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT CHAR8 **AsciiSymlink,
OUT UINT32 *AsciiSymlinkSize
)
{
UINT32 SymlinkSize;
CHAR8 *AsciiSymlinkTmp;
//
// Fast-symlink's EXT4_INODE_SIZE is not necessarily validated when we checked it in
// Ext4SymlinkIsFastSymlink(), so truncate if necessary.
//
SymlinkSize = (UINT32)MIN (EXT4_INODE_SIZE (File->Inode), EXT4_FAST_SYMLINK_MAX_SIZE);
AsciiSymlinkTmp = AllocatePool (SymlinkSize + 1);
if (AsciiSymlinkTmp == NULL) {
DEBUG ((DEBUG_ERROR, "[ext4] Failed to allocate symlink ascii string buffer\n"));
return EFI_OUT_OF_RESOURCES;
}
CopyMem (AsciiSymlinkTmp, File->Inode->i_data, SymlinkSize);
//
// Add null-terminator
//
AsciiSymlinkTmp[SymlinkSize] = '\0';
*AsciiSymlink = AsciiSymlinkTmp;
*AsciiSymlinkSize = SymlinkSize + 1;
return EFI_SUCCESS;
}
/**
Reads a slow symlink file.
@param[in] Partition Pointer to the ext4 partition.
@param[in] File Pointer to the open symlink file.
@param[out] AsciiSymlink Pointer to the output ascii symlink string.
@param[out] AsciiSymlinkSize Pointer to the output ascii symlink string length.
@retval EFI_SUCCESS Slow symlink was read.
@retval EFI_OUT_OF_RESOURCES Memory allocation error.
@retval EFI_INVALID_PARAMETER Slow symlink path has incorrect length
@retval EFI_VOLUME_CORRUPTED Symlink read block size differ from inode value
**/
STATIC
EFI_STATUS
Ext4ReadSlowSymlink (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT CHAR8 **AsciiSymlink,
OUT UINT32 *AsciiSymlinkSize
)
{
EFI_STATUS Status;
CHAR8 *SymlinkTmp;
UINT64 SymlinkSizeTmp;
UINT32 SymlinkAllocateSize;
UINTN ReadSize;
SymlinkSizeTmp = EXT4_INODE_SIZE (File->Inode);
//
// Allocate EXT4_INODE_SIZE + 1
//
if (SymlinkSizeTmp >= EXT4_EFI_PATH_MAX) {
DEBUG ((
DEBUG_WARN,
"[ext4] Warn: symlink path maximum length was hit!\n"
));
return EFI_INVALID_PARAMETER;
}
SymlinkAllocateSize = (UINT32)SymlinkSizeTmp + 1;
SymlinkTmp = AllocatePool (SymlinkAllocateSize);
if (SymlinkTmp == NULL) {
DEBUG ((DEBUG_FS, "[ext4] Failed to allocate symlink ascii string buffer\n"));
return EFI_OUT_OF_RESOURCES;
}
ReadSize = (UINTN)SymlinkSizeTmp;
Status = Ext4Read (Partition, File, SymlinkTmp, File->Position, &ReadSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_FS, "[ext4] Failed to read symlink from blocks with status %r\n", Status));
FreePool (SymlinkTmp);
return Status;
}
if (SymlinkSizeTmp != ReadSize) {
DEBUG ((
DEBUG_FS,
"[ext4] Error! The size of the read block doesn't match the value from the inode!\n"
));
FreePool (SymlinkTmp);
return EFI_VOLUME_CORRUPTED;
}
//
// Add null-terminator
//
SymlinkTmp[ReadSize] = '\0';
*AsciiSymlinkSize = SymlinkAllocateSize;
*AsciiSymlink = SymlinkTmp;
return EFI_SUCCESS;
}
/**
Reads a symlink file.
@param[in] Partition Pointer to the ext4 partition.
@param[in] File Pointer to the open symlink file.
@param[out] Symlink Pointer to the output unicode symlink string.
@retval EFI_SUCCESS Symlink was read.
@retval EFI_ACCESS_DENIED Symlink is encrypted.
@retval EFI_OUT_OF_RESOURCES Memory allocation error.
@retval EFI_INVALID_PARAMETER Symlink path has incorrect length
@retval EFI_VOLUME_CORRUPTED Symlink read block size differ from inode value
**/
EFI_STATUS
Ext4ReadSymlink (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT CHAR16 **Symlink
)
{
EFI_STATUS Status;
CHAR8 *SymlinkTmp;
UINT32 SymlinkSize;
CHAR16 *Symlink16Tmp;
CHAR16 *Needle;
//
// Assume that we already read Inode via Ext4ReadInode
// Skip reading, just check encryption flag
//
if ((File->Inode->i_flags & EXT4_ENCRYPT_FL) != 0) {
DEBUG ((DEBUG_WARN, "[ext4] Warn: symlink is encrypted\n"));
return EFI_ACCESS_DENIED;
}
if (Ext4SymlinkIsFastSymlink (File)) {
Status = Ext4ReadFastSymlink (Partition, File, &SymlinkTmp, &SymlinkSize);
} else {
Status = Ext4ReadSlowSymlink (Partition, File, &SymlinkTmp, &SymlinkSize);
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_FS, "[ext4] Symlink read error with Status %r\n", Status));
return Status;
}
Symlink16Tmp = AllocatePool (SymlinkSize * sizeof (CHAR16));
if (Symlink16Tmp == NULL) {
DEBUG ((DEBUG_FS, "[ext4] Failed to allocate symlink unicode string buffer\n"));
FreePool (SymlinkTmp);
return EFI_OUT_OF_RESOURCES;
}
Status = AsciiStrToUnicodeStrS (
SymlinkTmp,
Symlink16Tmp,
SymlinkSize
);
FreePool (SymlinkTmp);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_FS,
"[ext4] Failed to convert ascii symlink to unicode with Status %r\n",
Status
));
FreePool (Symlink16Tmp);
return Status;
}
//
// Convert to UEFI slashes
//
for (Needle = Symlink16Tmp; *Needle != L'\0'; Needle++) {
if (*Needle == L'/') {
*Needle = L'\\';
}
}
*Symlink = Symlink16Tmp;
return Status;
}

11
Ext4Pkg/Ext4Libs.dsc.inc Normal file
View File

@ -0,0 +1,11 @@
## @file
# Ext4 DSC include file for [LibraryClasses] section of all Architectures.
#
# SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
!if $(EXT4_ENABLE) == TRUE
BaseUcs2Utf8Lib|RedfishPkg/Library/BaseUcs2Utf8Lib/BaseUcs2Utf8Lib.inf
!endif

65
Ext4Pkg/Ext4Pkg.ci.yaml Normal file
View File

@ -0,0 +1,65 @@
## @file
# CI configuration for Ext4Pkg
#
# Copyright (c) Microsoft Corporation
# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
{
"LicenseCheck": {
"IgnoreFiles": []
},
"EccCheck": {
## Exception sample looks like below:
## "ExceptionList": [
## "<ErrorID>", "<KeyWord>"
## ]
"ExceptionList": [
],
## Both file path and directory path are accepted.
"IgnoreFiles": [
]
},
"CompilerPlugin": {
"DscPath": "Ext4Pkg.dsc"
},
"CharEncodingCheck": {
"IgnoreFiles": []
},
"DependencyCheck": {
"AcceptableDependencies": [
"MdePkg/MdePkg.dec",
"MdeModulePkg/MdeModulePkg.dec",
],
# For host based unit tests
"AcceptableDependencies-HOST_APPLICATION":[],
# For UEFI shell based apps
"AcceptableDependencies-UEFI_APPLICATION":[],
"IgnoreInf": []
},
"DscCompleteCheck": {
"IgnoreInf": [],
"DscPath": "Ext4Pkg.dsc"
},
"GuidCheck": {
"IgnoreGuidName": [],
"IgnoreGuidValue": [],
"IgnoreFoldersAndFiles": []
},
"LibraryClassCheck": {
"IgnoreHeaderFile": []
},
"SpellCheck": {
"ExtendWords": [
"ELTORITO",
"FHAND",
"IFILE",
"OFILE",
"FDISKed",
"Lfnbuffer",
"FFFFFFFFL",
"CDVOL",
"DMDEPKG"
]
}
}

17
Ext4Pkg/Ext4Pkg.dec Normal file
View File

@ -0,0 +1,17 @@
## @file
# Ext4 Package
#
# This package provides libraries and drivers related to the ext4 filesystem implementation.
# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
#
# Copyright (c) 2021 Pedro Falcato
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
DEC_SPECIFICATION = 0x00010005
PACKAGE_NAME = Ext4Pkg
PACKAGE_UNI_FILE = Ext4Pkg.uni
PACKAGE_GUID = 6B4BF998-668B-46D3-BCFA-971F99F8708C
PACKAGE_VERSION = 0.1

75
Ext4Pkg/Ext4Pkg.dsc Normal file
View File

@ -0,0 +1,75 @@
## @file
# Ext4 Package
#
# This package provides libraries and drivers related to the ext4 filesystem implementation.
# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
#
# Copyright (c) 2021 Pedro Falcato
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
PLATFORM_NAME = Ext4
PLATFORM_GUID = 6B4BF998-668B-46D3-BCFA-971F99F8708C
PLATFORM_VERSION = 0.1
DSC_SPECIFICATION = 0x00010005
SUPPORTED_ARCHITECTURES = IA32|X64|EBC|ARM|AARCH64|RISCV64
OUTPUT_DIRECTORY = Build/Ext4Pkg
BUILD_TARGETS = DEBUG|RELEASE|NOOPT
SKUID_IDENTIFIER = DEFAULT
!include MdePkg/MdeLibs.dsc.inc
[BuildOptions]
*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
[LibraryClasses]
#
# Entry Point Libraries
#
UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
#
# Common Libraries
#
BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
BaseUcs2Utf8Lib|RedfishPkg/Library/BaseUcs2Utf8Lib/BaseUcs2Utf8Lib.inf
#
# Required for stack protector support
#
NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
###################################################################################################
#
# Components Section - list of the modules and components that will be processed by compilation
# tools and the EDK II tools to generate PE32/PE32+/Coff image files.
#
# Note: The EDK II DSC file is not used to specify how compiled binary images get placed
# into firmware volume images. This section is just a list of modules to compile from
# source into UEFI-compliant binaries.
# It is the FDF file that contains information on combining binary files into firmware
# volume images, whose concept is beyond UEFI and is described in PI specification.
# Binary modules do not need to be listed in this section, as they should be
# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),
# Logo (Logo.bmp), and etc.
# There may also be modules listed in this section that are not required in the FDF file,
# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be
# generated for it, but the binary will not be put into any firmware volume.
#
###################################################################################################
[Components]
Ext4Pkg/Ext4Dxe/Ext4Dxe.inf

14
Ext4Pkg/Ext4Pkg.uni Normal file
View File

@ -0,0 +1,14 @@
## @file
# Ext4 Package
#
# This package provides libraries and drivers related to the ext4 filesystem implementation.
# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
#
# Copyright (c) 2021 Pedro Falcato
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
#string STR_PACKAGE_ABSTRACT #language en-US "Module implementations for the EXT4 file system"
#string STR_PACKAGE_DESCRIPTION #language en-US "This package contains UEFI drivers and libraries for the EXT4 file system."

View File

@ -0,0 +1,68 @@
/** @file
Variable Flash Information Library
Copyright (c) Microsoft Corporation<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef VARIABLE_FLASH_INFO_LIB_H_
#define VARIABLE_FLASH_INFO_LIB_H_
/**
Get the base address and size for the NV storage area used for UEFI variable storage.
@param[out] BaseAddress The NV storage base address.
@param[out] Length The NV storage length in bytes.
@retval EFI_SUCCESS NV storage information was found successfully.
@retval EFI_INVALID_PARAMETER A required pointer parameter is NULL.
@retval EFI_NOT_FOUND NV storage information could not be found.
**/
EFI_STATUS
EFIAPI
GetVariableFlashNvStorageInfo (
OUT EFI_PHYSICAL_ADDRESS *BaseAddress,
OUT UINT64 *Length
);
/**
Get the base address and size for the fault tolerant write (FTW) spare
area used for UEFI variable storage.
@param[out] BaseAddress The FTW spare base address.
@param[out] Length The FTW spare length in bytes.
@retval EFI_SUCCESS FTW spare information was found successfully.
@retval EFI_INVALID_PARAMETER A required pointer parameter is NULL.
@retval EFI_NOT_FOUND FTW spare information could not be found.
**/
EFI_STATUS
EFIAPI
GetVariableFlashFtwSpareInfo (
OUT EFI_PHYSICAL_ADDRESS *BaseAddress,
OUT UINT64 *Length
);
/**
Get the base address and size for the fault tolerant write (FTW) working
area used for UEFI variable storage.
@param[out] BaseAddress The FTW working area base address.
@param[out] Length The FTW working area length in bytes.
@retval EFI_SUCCESS FTW working information was found successfully.
@retval EFI_INVALID_PARAMETER A required pointer parameter is NULL.
@retval EFI_NOT_FOUND FTW working information could not be found.
**/
EFI_STATUS
EFIAPI
GetVariableFlashFtwWorkingInfo (
OUT EFI_PHYSICAL_ADDRESS *BaseAddress,
OUT UINT64 *Length
);
#endif

View File

@ -0,0 +1,161 @@
/** @file -- VariablePolicyHelperLib.h
This library contains helper functions for marshalling and registering
new policies with the VariablePolicy infrastructure.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef _EDKII_VARIABLE_POLICY_HELPER_LIB_H_
#define _EDKII_VARIABLE_POLICY_HELPER_LIB_H_
#include <Protocol/VariablePolicy.h>
/**
This helper function will allocate and populate a new VariablePolicy
structure for a policy that does not contain any sub-structures (such as
VARIABLE_LOCK_ON_VAR_STATE_POLICY).
NOTE: Caller will need to free structure once finished.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name.
Otherwise, will create a policy that targets an entire namespace.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] LockPolicyType LockPolicyType for the VariablePolicy.
@param[out] NewEntry If successful, will be set to a pointer to the allocated buffer containing the
new policy.
@retval EFI_SUCCESS Operation completed successfully and structure is populated.
@retval EFI_INVALID_PARAMETER Namespace is NULL.
@retval EFI_INVALID_PARAMETER LockPolicyType is invalid for a basic structure.
@retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in UINT16 size.
@retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space for structure.
**/
EFI_STATUS
EFIAPI
CreateBasicVariablePolicy (
IN CONST EFI_GUID *Namespace,
IN CONST CHAR16 *Name OPTIONAL,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN UINT8 LockPolicyType,
OUT VARIABLE_POLICY_ENTRY **NewEntry
);
/**
This helper function will allocate and populate a new VariablePolicy
structure for a policy with a lock type of VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE.
NOTE: Caller will need to free structure once finished.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name.
Otherwise, will create a policy that targets an entire namespace.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] VarStateNamespace Pointer to the EFI_GUID for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace.
@param[in] VarStateValue Value for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value.
@param[in] VarStateName Pointer to the CHAR16 array for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name.
@param[out] NewEntry If successful, will be set to a pointer to the allocated buffer containing the
new policy.
@retval EFI_SUCCESS Operation completed successfully and structure is populated.
@retval EFI_INVALID_PARAMETER Namespace, VarStateNamespace, VarStateName is NULL.
@retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in UINT16 size.
@retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space for structure.
**/
EFI_STATUS
EFIAPI
CreateVarStateVariablePolicy (
IN CONST EFI_GUID *Namespace,
IN CONST CHAR16 *Name OPTIONAL,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN CONST EFI_GUID *VarStateNamespace,
IN UINT8 VarStateValue,
IN CONST CHAR16 *VarStateName,
OUT VARIABLE_POLICY_ENTRY **NewEntry
);
/**
This helper function does everything that CreateBasicVariablePolicy() does, but also
uses the passed in protocol to register the policy with the infrastructure.
Does not return a buffer, does not require the caller to free anything.
@param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy protocol.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name.
Otherwise, will create a policy that targets an entire namespace.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] LockPolicyType LockPolicyType for the VariablePolicy.
@retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL.
@retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or RegisterVariablePolicy().
**/
EFI_STATUS
EFIAPI
RegisterBasicVariablePolicy (
IN EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy,
IN CONST EFI_GUID *Namespace,
IN CONST CHAR16 *Name OPTIONAL,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN UINT8 LockPolicyType
);
/**
This helper function does everything that CreateBasicVariablePolicy() does, but also
uses the passed in protocol to register the policy with the infrastructure.
Does not return a buffer, does not require the caller to free anything.
@param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy protocol.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name.
Otherwise, will create a policy that targets an entire namespace.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] VarStateNamespace Pointer to the EFI_GUID for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace.
@param[in] VarStateName Pointer to the CHAR16 array for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name.
@param[in] VarStateValue Value for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value.
@retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL.
@retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or RegisterVariablePolicy().
**/
EFI_STATUS
EFIAPI
RegisterVarStateVariablePolicy (
IN EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy,
IN CONST EFI_GUID *Namespace,
IN CONST CHAR16 *Name OPTIONAL,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN CONST EFI_GUID *VarStateNamespace,
IN CONST CHAR16 *VarStateName,
IN UINT8 VarStateValue
);
#endif // _EDKII_VARIABLE_POLICY_HELPER_LIB_H_

View File

@ -0,0 +1,304 @@
/** @file -- VariablePolicyLib.h
Business logic for Variable Policy enforcement.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef _VARIABLE_POLICY_LIB_H_
#define _VARIABLE_POLICY_LIB_H_
#include <Protocol/VariablePolicy.h>
/**
This API function validates and registers a new policy with
the policy enforcement engine.
@param[in] NewPolicy Pointer to the incoming policy structure.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
@retval EFI_ALREADY_STARTED An identical matching policy already exists.
@retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
@retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
@retval EFI_ABORTED A calculation error has prevented this function from completing.
@retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
@retval EFI_NOT_READY Library has not yet been initialized.
**/
EFI_STATUS
EFIAPI
RegisterVariablePolicy (
IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
);
/**
This API function checks to see whether the parameters to SetVariable would
be allowed according to the current variable policies.
@param[in] VariableName Same as EFI_SET_VARIABLE.
@param[in] VendorGuid Same as EFI_SET_VARIABLE.
@param[in] Attributes Same as EFI_SET_VARIABLE.
@param[in] DataSize Same as EFI_SET_VARIABLE.
@param[in] Data Same as EFI_SET_VARIABLE.
@retval EFI_SUCCESS A matching policy allows this update.
@retval EFI_SUCCESS There are currently no policies that restrict this update.
@retval EFI_SUCCESS The protections have been disable until the next reboot.
@retval EFI_WRITE_PROTECTED Variable is currently locked.
@retval EFI_INVALID_PARAMETER Attributes or size are invalid.
@retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.
@retval EFI_NOT_READY Library has not been initialized.
**/
EFI_STATUS
EFIAPI
ValidateSetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
);
/**
This API function disables the variable policy enforcement. If it's
already been called once, will return EFI_ALREADY_STARTED.
@retval EFI_SUCCESS
@retval EFI_ALREADY_STARTED Has already been called once this boot.
@retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
@retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
@retval EFI_NOT_READY Library has not yet been initialized.
**/
EFI_STATUS
EFIAPI
DisableVariablePolicy (
VOID
);
/**
This API function will dump the entire contents of the variable policy table.
Similar to GetVariable, the first call can be made with a 0 size and it will return
the size of the buffer required to hold the entire table.
@param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
@param[in,out] Size On input, the size of the output buffer. On output, the size
of the data returned.
@retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
@retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
@retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
@retval EFI_NOT_READY Library has not yet been initialized.
**/
EFI_STATUS
EFIAPI
DumpVariablePolicy (
OUT UINT8 *Policy,
IN OUT UINT32 *Size
);
/**
This function will return variable policy information for a UEFI variable with a
registered variable policy.
@param[in] VariableName The name of the variable to use for the policy search.
@param[in] VendorGuid The vendor GUID of the variable to use for the policy search.
@param[in,out] VariablePolicyVariableNameBufferSize On input, the size, in bytes, of the VariablePolicyVariableName
buffer.
On output, the size, in bytes, needed to store the variable
policy variable name.
If testing for the VariablePolicyVariableName buffer size
needed, set this value to zero so EFI_BUFFER_TOO_SMALL is
guaranteed to be returned if the variable policy variable name
is found.
@param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
if found.
@param[out] VariablePolicyVariableName Pointer to a buffer where the variable name used for the
variable policy will be written if a variable name is
registered.
If the variable policy is not associated with a variable name
(e.g. applied to variable vendor namespace) and this parameter
is given, this parameter will not be modified and
VariablePolicyVariableNameBufferSize will be set to zero to
indicate a name was not present.
If the pointer given is not NULL,
VariablePolicyVariableNameBufferSize must be non-NULL.
@retval EFI_SUCCESS A variable policy entry was found and returned successfully.
@retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
@retval EFI_BUFFER_TOO_SMALL The VariablePolicyVariableName buffer value is too small for the size needed.
The buffer should now point to the size needed.
@retval EFI_NOT_READY Variable policy has not yet been initialized.
@retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
VariablePolicyVariableName is non-NULL and VariablePolicyVariableNameBufferSize
is NULL.
@retval EFI_NOT_FOUND A variable policy was not found for the given UEFI variable name and vendor GUID.
**/
EFI_STATUS
EFIAPI
GetVariablePolicyInfo (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN OUT UINTN *VariablePolicyVariableNameBufferSize OPTIONAL,
OUT VARIABLE_POLICY_ENTRY *VariablePolicy,
OUT CHAR16 *VariablePolicyVariableName OPTIONAL
);
/**
This function will return the Lock on Variable State policy information for the policy
associated with the given UEFI variable.
@param[in] VariableName The name of the variable to use for the policy search.
@param[in] VendorGuid The vendor GUID of the variable to use for the policy
search.
@param[in,out] VariableLockPolicyVariableNameBufferSize On input, the size, in bytes, of the
VariableLockPolicyVariableName buffer.
On output, the size, in bytes, needed to store the variable
policy variable name.
If testing for the VariableLockPolicyVariableName buffer
size needed, set this value to zero so EFI_BUFFER_TOO_SMALL
is guaranteed to be returned if the variable policy variable
name is found.
@param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
if found.
@param[out] VariableLockPolicyVariableName Pointer to a buffer where the variable name used for the
variable lock on variable state policy will be written if
a variable name is registered.
If the lock on variable policy is not associated with a
variable name (e.g. applied to variable vendor namespace)
and this parameter is given, this parameter will not be
modified and VariableLockPolicyVariableNameBufferSize will
be set to zero to indicate a name was not present.
If the pointer given is not NULL,
VariableLockPolicyVariableNameBufferSize must be non-NULL.
@retval EFI_SUCCESS A Lock on Variable State variable policy entry was found and returned
successfully.
@retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
@retval EFI_BUFFER_TOO_SMALL The VariableLockPolicyVariableName buffer is too small for the size needed.
The buffer should now point to the size needed.
@retval EFI_NOT_READY Variable policy has not yet been initialized.
@retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
VariableLockPolicyVariableName is non-NULL and
VariableLockPolicyVariableNameBufferSize is NULL.
@retval EFI_NOT_FOUND A Lock on Variable State variable policy was not found for the given UEFI
variable name and vendor GUID.
**/
EFI_STATUS
EFIAPI
GetLockOnVariableStateVariablePolicyInfo (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN OUT UINTN *VariableLockPolicyVariableNameBufferSize OPTIONAL,
OUT VARIABLE_LOCK_ON_VAR_STATE_POLICY *VariablePolicy,
OUT CHAR16 *VariableLockPolicyVariableName OPTIONAL
);
/**
This API function returns whether or not the policy engine is
currently being enforced.
@retval TRUE
@retval FALSE
@retval FALSE Library has not yet been initialized.
**/
BOOLEAN
EFIAPI
IsVariablePolicyEnabled (
VOID
);
/**
This API function locks the interface so that no more policy updates
can be performed or changes made to the enforcement until the next boot.
@retval EFI_SUCCESS
@retval EFI_NOT_READY Library has not yet been initialized.
**/
EFI_STATUS
EFIAPI
LockVariablePolicy (
VOID
);
/**
This API function returns whether or not the policy interface is locked
for the remainder of the boot.
@retval TRUE
@retval FALSE
@retval FALSE Library has not yet been initialized.
**/
BOOLEAN
EFIAPI
IsVariablePolicyInterfaceLocked (
VOID
);
/**
This helper function initializes the library and sets
up any required internal structures or handlers.
Also registers the internal pointer for the GetVariable helper.
@param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to
check policy criteria that involve the existence of other variables.
@retval EFI_SUCCESS
@retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to
deinitialize.
**/
EFI_STATUS
EFIAPI
InitVariablePolicyLib (
IN EFI_GET_VARIABLE GetVariableHelper
);
/**
This helper function returns whether or not the library is currently initialized.
@retval TRUE
@retval FALSE
**/
BOOLEAN
EFIAPI
IsVariablePolicyLibInitialized (
VOID
);
/**
This helper function tears down the library.
Should generally only be used for test harnesses.
@retval EFI_SUCCESS
@retval EFI_NOT_READY Deinitialize was called without first calling initialize.
**/
EFI_STATUS
EFIAPI
DeinitVariablePolicyLib (
VOID
);
#endif // _VARIABLE_POLICY_LIB_H_

View File

@ -0,0 +1,276 @@
/** @file -- VariablePolicy.h
This protocol allows communication with Variable Policy Engine.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef __EDKII_VARIABLE_POLICY_PROTOCOL__
#define __EDKII_VARIABLE_POLICY_PROTOCOL__
#define EDKII_VARIABLE_POLICY_PROTOCOL_REVISION 0x0000000000020000
/*
Rev 0x0000000000010000:
- Initial protocol definition
Rev 0x0000000000020000:
- Add GetVariablePolicyInfo() API
- Add GetLockOnVariableStateVariablePolicyInfo() API
*/
#define EDKII_VARIABLE_POLICY_PROTOCOL_GUID \
{ \
0x81D1675C, 0x86F6, 0x48DF, { 0xBD, 0x95, 0x9A, 0x6E, 0x4F, 0x09, 0x25, 0xC3 } \
}
#define VARIABLE_POLICY_ENTRY_REVISION 0x00010000
#pragma pack(push, 1)
typedef struct {
UINT32 Version;
UINT16 Size;
UINT16 OffsetToName;
EFI_GUID Namespace;
UINT32 MinSize;
UINT32 MaxSize;
UINT32 AttributesMustHave;
UINT32 AttributesCantHave;
UINT8 LockPolicyType;
UINT8 Padding[3];
// UINT8 LockPolicy[]; // Variable Length Field
// CHAR16 Name[] // Variable Length Field
} VARIABLE_POLICY_ENTRY;
#define VARIABLE_POLICY_NO_MIN_SIZE 0
#define VARIABLE_POLICY_NO_MAX_SIZE MAX_UINT32
#define VARIABLE_POLICY_NO_MUST_ATTR 0
#define VARIABLE_POLICY_NO_CANT_ATTR 0
#define VARIABLE_POLICY_TYPE_NO_LOCK 0
#define VARIABLE_POLICY_TYPE_LOCK_NOW 1
#define VARIABLE_POLICY_TYPE_LOCK_ON_CREATE 2
#define VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE 3
typedef struct {
EFI_GUID Namespace;
UINT8 Value;
UINT8 Padding;
// CHAR16 Name[]; // Variable Length Field
} VARIABLE_LOCK_ON_VAR_STATE_POLICY;
#pragma pack(pop)
/**
This API function disables the variable policy enforcement. If it's
already been called once, will return EFI_ALREADY_STARTED.
@retval EFI_SUCCESS
@retval EFI_ALREADY_STARTED Has already been called once this boot.
@retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
@retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
**/
typedef
EFI_STATUS
(EFIAPI *DISABLE_VARIABLE_POLICY)(
VOID
);
/**
This API function returns whether or not the policy engine is
currently being enforced.
@param[out] State Pointer to a return value for whether the policy enforcement
is currently enabled.
@retval EFI_SUCCESS
@retval Others An error has prevented this command from completing.
**/
typedef
EFI_STATUS
(EFIAPI *IS_VARIABLE_POLICY_ENABLED)(
OUT BOOLEAN *State
);
/**
This API function validates and registers a new policy with
the policy enforcement engine.
@param[in] NewPolicy Pointer to the incoming policy structure.
@retval EFI_SUCCESS
@retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
@retval EFI_ALREADY_STARTED An identical matching policy already exists.
@retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
@retval EFI_ABORTED A calculation error has prevented this function from completing.
@retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
**/
typedef
EFI_STATUS
(EFIAPI *REGISTER_VARIABLE_POLICY)(
IN CONST VARIABLE_POLICY_ENTRY *PolicyEntry
);
/**
This API function will dump the entire contents of the variable policy table.
Similar to GetVariable, the first call can be made with a 0 size and it will return
the size of the buffer required to hold the entire table.
@param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
@param[in,out] Size On input, the size of the output buffer. On output, the size
of the data returned.
@retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
@retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
@retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
**/
typedef
EFI_STATUS
(EFIAPI *DUMP_VARIABLE_POLICY)(
IN OUT UINT8 *Policy,
IN OUT UINT32 *Size
);
/**
This API function locks the interface so that no more policy updates
can be performed or changes made to the enforcement until the next boot.
@retval EFI_SUCCESS
@retval Others An error has prevented this command from completing.
**/
typedef
EFI_STATUS
(EFIAPI *LOCK_VARIABLE_POLICY)(
VOID
);
/**
This function will return variable policy information for a UEFI variable with a
registered variable policy.
@param[in] VariableName The name of the variable to use for the policy search.
@param[in] VendorGuid The vendor GUID of the variable to use for the policy search.
@param[in,out] VariablePolicyVariableNameBufferSize On input, the size, in bytes, of the VariablePolicyVariableName
buffer.
On output, the size, in bytes, needed to store the variable
policy variable name.
If testing for the VariablePolicyVariableName buffer size
needed, set this value to zero so EFI_BUFFER_TOO_SMALL is
guaranteed to be returned if the variable policy variable name
is found.
@param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
if found.
@param[out] VariablePolicyVariableName Pointer to a buffer where the variable name used for the
variable policy will be written if a variable name is
registered.
If the variable policy is not associated with a variable name
(e.g. applied to variable vendor namespace) and this parameter
is given, this parameter will not be modified and
VariablePolicyVariableNameBufferSize will be set to zero to
indicate a name was not present.
If the pointer given is not NULL,
VariablePolicyVariableNameBufferSize must be non-NULL.
@retval EFI_SUCCESS A variable policy entry was found and returned successfully.
@retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
@retval EFI_BUFFER_TOO_SMALL The VariablePolicyVariableName buffer value is too small for the size needed.
The buffer should now point to the size needed.
@retval EFI_NOT_READY Variable policy has not yet been initialized.
@retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
VariablePolicyVariableName is non-NULL and VariablePolicyVariableNameBufferSize
is NULL.
@retval EFI_NOT_FOUND A variable policy was not found for the given UEFI variable name and vendor GUID.
**/
typedef
EFI_STATUS
(EFIAPI *GET_VARIABLE_POLICY_INFO)(
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN OUT UINTN *VariablePolicyVariableNameBufferSize OPTIONAL,
OUT VARIABLE_POLICY_ENTRY *VariablePolicy,
OUT CHAR16 *VariablePolicyVariableName OPTIONAL
);
/**
This function will return the Lock on Variable State policy information for the policy
associated with the given UEFI variable.
@param[in] VariableName The name of the variable to use for the policy search.
@param[in] VendorGuid The vendor GUID of the variable to use for the policy
search.
@param[in,out] VariableLockPolicyVariableNameBufferSize On input, the size, in bytes, of the
VariableLockPolicyVariableName buffer.
On output, the size, in bytes, needed to store the variable
policy variable name.
If testing for the VariableLockPolicyVariableName buffer
size needed, set this value to zero so EFI_BUFFER_TOO_SMALL
is guaranteed to be returned if the variable policy variable
name is found.
@param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
if found.
@param[out] VariableLockPolicyVariableName Pointer to a buffer where the variable name used for the
variable lock on variable state policy will be written if
a variable name is registered.
If the lock on variable policy is not associated with a
variable name (e.g. applied to variable vendor namespace)
and this parameter is given, this parameter will not be
modified and VariableLockPolicyVariableNameBufferSize will
be set to zero to indicate a name was not present.
If the pointer given is not NULL,
VariableLockPolicyVariableNameBufferSize must be non-NULL.
@retval EFI_SUCCESS A Lock on Variable State variable policy entry was found and returned
successfully.
@retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
@retval EFI_BUFFER_TOO_SMALL The VariableLockPolicyVariableName buffer is too small for the size needed.
The buffer should now point to the size needed.
@retval EFI_NOT_READY Variable policy has not yet been initialized.
@retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
VariableLockPolicyVariableName is non-NULL and
VariableLockPolicyVariableNameBufferSize is NULL.
@retval EFI_NOT_FOUND A Lock on Variable State variable policy was not found for the given UEFI
variable name and vendor GUID.
**/
typedef
EFI_STATUS
(EFIAPI *GET_LOCK_ON_VARIABLE_STATE_VARIABLE_POLICY_INFO)(
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN OUT UINTN *VariableLockPolicyVariableNameBufferSize OPTIONAL,
OUT VARIABLE_LOCK_ON_VAR_STATE_POLICY *VariablePolicy,
OUT CHAR16 *VariableLockPolicyVariableName OPTIONAL
);
typedef struct {
UINT64 Revision;
DISABLE_VARIABLE_POLICY DisableVariablePolicy;
IS_VARIABLE_POLICY_ENABLED IsVariablePolicyEnabled;
REGISTER_VARIABLE_POLICY RegisterVariablePolicy;
DUMP_VARIABLE_POLICY DumpVariablePolicy;
LOCK_VARIABLE_POLICY LockVariablePolicy;
GET_VARIABLE_POLICY_INFO GetVariablePolicyInfo;
GET_LOCK_ON_VARIABLE_STATE_VARIABLE_POLICY_INFO GetLockOnVariableStateVariablePolicyInfo;
} _EDKII_VARIABLE_POLICY_PROTOCOL;
typedef _EDKII_VARIABLE_POLICY_PROTOCOL EDKII_VARIABLE_POLICY_PROTOCOL;
extern EFI_GUID gEdkiiVariablePolicyProtocolGuid;
#endif

View File

@ -31,3 +31,4 @@
[LibraryClasses]
DebugLib
CommonMemoryAllocationLib

View File

@ -1,13 +1,12 @@
/** @file
Null routines for memory profile for DxeCore.
Null routines for memory profile.
Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiDxe.h>
#include <Uefi.h>
#include <Guid/MemoryProfile.h>
@ -36,14 +35,13 @@
EFI_STATUS
EFIAPI
MemoryProfileLibRecord (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
)
{
return EFI_UNSUPPORTED;
}

View File

@ -0,0 +1,32 @@
## @file
# Memory Profile Library Null instance.
#
# Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = BaseMemoryProfileLibNull
MODULE_UNI_FILE = BaseMemoryProfileLibNull.uni
FILE_GUID = 888809d7-e5c5-4848-b8b9-f920a7b89ce1
MODULE_TYPE = BASE
VERSION_STRING = 1.0
LIBRARY_CLASS = MemoryProfileLib
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 EBC
#
[Sources]
BaseMemoryProfileLibNull.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec

View File

@ -0,0 +1,14 @@
// /** @file
// Memory Profile Library Null instance.
//
// Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Memory Profile Library Null instance"
#string STR_MODULE_DESCRIPTION #language en-US "Memory Profile Library Null instance."

View File

@ -0,0 +1,179 @@
/** @file
Variable Flash Information Library
Copyright (c) Microsoft Corporation<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Pi/PiMultiPhase.h>
#include <Guid/VariableFlashInfo.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/VariableFlashInfoLib.h>
/**
Get the HOB that contains variable flash information.
@param[out] VariableFlashInfo Pointer to a pointer to set to the variable flash information structure.
@retval EFI_SUCCESS Variable flash information was found successfully.
@retval EFI_INVALID_PARAMETER The VariableFlashInfo pointer given is NULL.
@retval EFI_NOT_FOUND Variable flash information could not be found.
**/
STATIC
EFI_STATUS
GetVariableFlashInfoFromHob (
OUT VARIABLE_FLASH_INFO **VariableFlashInfo
)
{
EFI_HOB_GUID_TYPE *GuidHob;
if (VariableFlashInfo == NULL) {
return EFI_INVALID_PARAMETER;
}
GuidHob = GetFirstGuidHob (&gVariableFlashInfoHobGuid);
if (GuidHob == NULL) {
return EFI_NOT_FOUND;
}
*VariableFlashInfo = GET_GUID_HOB_DATA (GuidHob);
//
// Assert if more than one variable flash information HOB is present.
//
DEBUG_CODE (
if ((GetNextGuidHob (&gVariableFlashInfoHobGuid, GET_NEXT_HOB (GuidHob)) != NULL)) {
DEBUG ((DEBUG_ERROR, "ERROR: Found two variable flash information HOBs\n"));
ASSERT (FALSE);
}
);
return EFI_SUCCESS;
}
/**
Get the base address and size for the NV storage area used for UEFI variable storage.
@param[out] BaseAddress The NV storage base address.
@param[out] Length The NV storage length in bytes.
@retval EFI_SUCCESS NV storage information was found successfully.
@retval EFI_INVALID_PARAMETER A required pointer parameter is NULL.
**/
EFI_STATUS
EFIAPI
GetVariableFlashNvStorageInfo (
OUT EFI_PHYSICAL_ADDRESS *BaseAddress,
OUT UINT64 *Length
)
{
EFI_STATUS Status;
VARIABLE_FLASH_INFO *VariableFlashInfo;
if ((BaseAddress == NULL) || (Length == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = GetVariableFlashInfoFromHob (&VariableFlashInfo);
if (!EFI_ERROR (Status)) {
*BaseAddress = VariableFlashInfo->NvVariableBaseAddress;
*Length = VariableFlashInfo->NvVariableLength;
} else {
*BaseAddress = (EFI_PHYSICAL_ADDRESS)(PcdGet64 (PcdFlashNvStorageVariableBase64) != 0 ?
PcdGet64 (PcdFlashNvStorageVariableBase64) :
PcdGet32 (PcdFlashNvStorageVariableBase)
);
*Length = (UINT64)PcdGet32 (PcdFlashNvStorageVariableSize);
}
return EFI_SUCCESS;
}
/**
Get the base address and size for the fault tolerant write (FTW) spare
area used for UEFI variable storage.
@param[out] BaseAddress The FTW spare base address.
@param[out] Length The FTW spare length in bytes.
@retval EFI_SUCCESS FTW spare information was found successfully.
@retval EFI_INVALID_PARAMETER A required pointer parameter is NULL.
@retval EFI_NOT_FOUND FTW spare information could not be found.
**/
EFI_STATUS
EFIAPI
GetVariableFlashFtwSpareInfo (
OUT EFI_PHYSICAL_ADDRESS *BaseAddress,
OUT UINT64 *Length
)
{
EFI_STATUS Status;
VARIABLE_FLASH_INFO *VariableFlashInfo;
if ((BaseAddress == NULL) || (Length == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = GetVariableFlashInfoFromHob (&VariableFlashInfo);
if (!EFI_ERROR (Status)) {
*BaseAddress = VariableFlashInfo->FtwSpareBaseAddress;
*Length = VariableFlashInfo->FtwSpareLength;
} else {
*BaseAddress = (EFI_PHYSICAL_ADDRESS)(PcdGet64 (PcdFlashNvStorageFtwSpareBase64) != 0 ?
PcdGet64 (PcdFlashNvStorageFtwSpareBase64) :
PcdGet32 (PcdFlashNvStorageFtwSpareBase)
);
*Length = (UINT64)PcdGet32 (PcdFlashNvStorageFtwSpareSize);
}
return EFI_SUCCESS;
}
/**
Get the base address and size for the fault tolerant write (FTW) working
area used for UEFI variable storage.
@param[out] BaseAddress The FTW working area base address.
@param[out] Length The FTW working area length in bytes.
@retval EFI_SUCCESS FTW working information was found successfully.
@retval EFI_INVALID_PARAMETER A required pointer parameter is NULL.
@retval EFI_NOT_FOUND FTW working information could not be found.
**/
EFI_STATUS
EFIAPI
GetVariableFlashFtwWorkingInfo (
OUT EFI_PHYSICAL_ADDRESS *BaseAddress,
OUT UINT64 *Length
)
{
EFI_STATUS Status;
VARIABLE_FLASH_INFO *VariableFlashInfo;
if ((BaseAddress == NULL) || (Length == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = GetVariableFlashInfoFromHob (&VariableFlashInfo);
if (!EFI_ERROR (Status)) {
*BaseAddress = VariableFlashInfo->FtwWorkingBaseAddress;
*Length = VariableFlashInfo->FtwWorkingLength;
} else {
*BaseAddress = (EFI_PHYSICAL_ADDRESS)(PcdGet64 (PcdFlashNvStorageFtwWorkingBase64) != 0 ?
PcdGet64 (PcdFlashNvStorageFtwWorkingBase64) :
PcdGet32 (PcdFlashNvStorageFtwWorkingBase)
);
*Length = (UINT64)PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
}
return EFI_SUCCESS;
}

View File

@ -0,0 +1,48 @@
## @file
# Variable Flash Information Library
#
# Provides services to access UEFI variable flash information.
#
# Copyright (c) Microsoft Corporation<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = BaseVariableFlashInfoLib
MODULE_UNI_FILE = BaseVariableFlashInfoLib.uni
FILE_GUID = DEC426C9-C92E-4BAD-8E93-3F61C261118B
MODULE_TYPE = BASE
VERSION_STRING = 1.0
LIBRARY_CLASS = VariableFlashInfoLib
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = ANY
#
[Sources]
BaseVariableFlashInfoLib.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
DebugLib
HobLib
[Guids]
gVariableFlashInfoHobGuid ## CONSUMES ## HOB
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize ## SOMETIMES_CONSUMES

View File

@ -0,0 +1,12 @@
// /** @file
// Variable Flash Information Library
//
// Copyright (c) Microsoft Corporation<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "UEFI variable flash information library"
#string STR_MODULE_DESCRIPTION #language en-US "Provides services to access UEFI variable flash information."

View File

@ -0,0 +1,84 @@
/** @file
Common code for the Memory Allocation Library aligned page allocation code.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Base.h>
#include <Uefi/UefiBaseType.h>
#include <Uefi/UefiSpec.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/MemoryAllocationLibEx.h>
#include "AlignedPages.h"
/**
Allocates one or more 4KB pages of a certain memory type at a specified alignment.
Allocates the number of 4KB pages specified by Pages of a certain memory type with an alignment
specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is returned.
If there is not enough memory at the specified alignment remaining to satisfy the request, then
NULL is returned.
If Alignment is not a power of two and Alignment is not zero, then ASSERT().
If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@param Alignment The requested alignment of the allocation. Must be a power of two.
If Alignment is zero, then byte alignment is used.
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
VOID *
InternalAllocateAlignedPages (
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN UINTN Alignment
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Memory;
Status = AllocateAlignedPagesEx (
AllocateAnyPages,
MemoryType,
Pages,
Alignment,
&Memory
);
if (EFI_ERROR (Status)) {
return NULL;
}
return (VOID *)(UINTN)Memory;
}
/**
Frees one or more 4KB pages that were previously allocated with one of the aligned page
allocation functions in the Memory Allocation Library.
Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer
must have been allocated on a previous call to the aligned page allocation services of the Memory
Allocation Library. If it is not possible to free allocated pages, then this function will
perform no actions.
If Buffer was not allocated with an aligned page allocation function in the Memory Allocation
Library, then ASSERT().
If Pages is zero, then ASSERT().
@param Buffer Pointer to the buffer of pages to free.
@param Pages The number of 4 KB pages to free.
**/
VOID
InternalFreeAlignedPages (
IN VOID *Buffer,
IN UINTN Pages
)
{
FreePages (Buffer, Pages);
}

View File

@ -0,0 +1,60 @@
/** @file
Common code for the Memory Allocation Library aligned page allocation code.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef ALIGNED_PAGES_H_
#define ALIGNED_PAGES_H_
/**
Allocates one or more 4KB pages of a certain memory type at a specified alignment.
Allocates the number of 4KB pages specified by Pages of a certain memory type with an alignment
specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is returned.
If there is not enough memory at the specified alignment remaining to satisfy the request, then
NULL is returned.
If Alignment is not a power of two and Alignment is not zero, then ASSERT().
If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@param Alignment The requested alignment of the allocation. Must be a power of two.
If Alignment is zero, then byte alignment is used.
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
VOID *
InternalAllocateAlignedPages (
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN UINTN Alignment
);
/**
Frees one or more 4KB pages that were previously allocated with one of the aligned page
allocation functions in the Memory Allocation Library.
Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer
must have been allocated on a previous call to the aligned page allocation services of the Memory
Allocation Library. If it is not possible to free allocated pages, then this function will
perform no actions.
If Buffer was not allocated with an aligned page allocation function in the Memory Allocation
Library, then ASSERT().
If Pages is zero, then ASSERT().
@param Buffer Pointer to the buffer of pages to free.
@param Pages The number of 4 KB pages to free.
**/
VOID
InternalFreeAlignedPages (
IN VOID *Buffer,
IN UINTN Pages
);
#endif // ALIGNED_PAGES_H_

View File

@ -1,55 +1,25 @@
/** @file
Support routines for memory allocation routines based
on boot services for Dxe phase drivers, with memory profile support.
Common code for the Memory Allocation Library based on
PhaseMemoryAllocationLib.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Base.h>
#include <Uefi/UefiBaseType.h>
#include <Uefi/UefiSpec.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLibEx.h>
#include <Library/PhaseMemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryProfileLib.h>
/**
Allocates one or more 4KB pages of a certain memory type.
Allocates the number of 4KB pages of a certain memory type and returns a pointer to the allocated
buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL is returned.
If there is not enough memory remaining to satisfy the request, then NULL is returned.
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
VOID *
InternalAllocatePages (
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Memory;
if (Pages == 0) {
return NULL;
}
Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);
if (EFI_ERROR(Status)) {
return NULL;
}
return (VOID *) (UINTN) Memory;
}
#include "AlignedPages.h"
/**
Allocates one or more 4KB pages of type EfiBootServicesData.
@ -70,19 +40,26 @@ AllocatePages (
IN UINTN Pages
)
{
VOID *Buffer;
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Memory;
VOID *Buffer;
Buffer = InternalAllocatePages (EfiBootServicesData, Pages);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_PAGES,
EfiBootServicesData,
Buffer,
EFI_PAGES_TO_SIZE (Pages),
NULL
);
Status = PhaseAllocatePages (AllocateAnyPages, gPhaseDefaultDataType, Pages, &Memory);
if (EFI_ERROR (Status)) {
return NULL;
}
Buffer = (VOID *)(UINTN)Memory;
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_PAGES,
gPhaseDefaultDataType,
Buffer,
EFI_PAGES_TO_SIZE (Pages),
NULL
);
return Buffer;
}
@ -105,19 +82,26 @@ AllocateRuntimePages (
IN UINTN Pages
)
{
VOID *Buffer;
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Memory;
VOID *Buffer;
Buffer = InternalAllocatePages (EfiRuntimeServicesData, Pages);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_PAGES,
EfiRuntimeServicesData,
Buffer,
EFI_PAGES_TO_SIZE (Pages),
NULL
);
Status = PhaseAllocatePages (AllocateAnyPages, EfiRuntimeServicesData, Pages, &Memory);
if (EFI_ERROR (Status)) {
return NULL;
}
Buffer = (VOID *)(UINTN)Memory;
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_PAGES,
EfiRuntimeServicesData,
Buffer,
EFI_PAGES_TO_SIZE (Pages),
NULL
);
return Buffer;
}
@ -140,22 +124,59 @@ AllocateReservedPages (
IN UINTN Pages
)
{
VOID *Buffer;
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Memory;
VOID *Buffer;
Buffer = InternalAllocatePages (EfiReservedMemoryType, Pages);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_PAGES,
EfiReservedMemoryType,
Buffer,
EFI_PAGES_TO_SIZE (Pages),
NULL
);
Status = PhaseAllocatePages (AllocateAnyPages, EfiReservedMemoryType, Pages, &Memory);
if (EFI_ERROR (Status)) {
return NULL;
}
Buffer = (VOID *)(UINTN)Memory;
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_PAGES,
EfiReservedMemoryType,
Buffer,
EFI_PAGES_TO_SIZE (Pages),
NULL
);
return Buffer;
}
/**
Allocates one or more 4KB pages of type EfiBootServicesCode.
Allocates the number of 4KB pages of type EfiBootServicesCode and returns a pointer to the
allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
is returned. If there is not enough memory remaining to satisfy the request, then NULL is
returned.
@param Pages The number of 4 KB pages to allocate.
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
VOID *
EFIAPI
AllocateCodePages (
IN UINTN Pages
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Memory;
Status = PhaseAllocatePages (AllocateAnyPages, gPhaseDefaultCodeType, Pages, &Memory);
if (EFI_ERROR (Status)) {
return NULL;
}
return (VOID *)(UINTN)Memory;
}
/**
Frees one or more 4KB pages that were previously allocated with one of the page allocation
functions in the Memory Allocation Library.
@ -169,7 +190,7 @@ AllocateReservedPages (
then ASSERT().
If Pages is zero, then ASSERT().
@param Buffer The pointer to the buffer of pages to free.
@param Buffer Pointer to the buffer of pages to free.
@param Pages The number of 4 KB pages to free.
**/
@ -180,97 +201,10 @@ FreePages (
IN UINTN Pages
)
{
EFI_STATUS Status;
EFI_STATUS Status;
ASSERT (Pages != 0);
Status = gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) Buffer, Pages);
ASSERT_EFI_ERROR(Status);
}
/**
Allocates one or more 4KB pages of a certain memory type at a specified alignment.
Allocates the number of 4KB pages specified by Pages of a certain memory type with an alignment
specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is returned.
If there is not enough memory at the specified alignment remaining to satisfy the request, then
NULL is returned.
If Alignment is not a power of two and Alignment is not zero, then ASSERT().
If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@param Alignment The requested alignment of the allocation. Must be a power of two.
If Alignment is zero, then byte alignment is used.
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
VOID *
InternalAllocateAlignedPages (
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN UINTN Alignment
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Memory;
UINTN AlignedMemory;
UINTN AlignmentMask;
UINTN UnalignedPages;
UINTN RealPages;
//
// Alignment must be a power of two or zero.
//
ASSERT ((Alignment & (Alignment - 1)) == 0);
if (Pages == 0) {
return NULL;
}
if (Alignment > EFI_PAGE_SIZE) {
//
// Calculate the total number of pages since alignment is larger than page size.
//
AlignmentMask = Alignment - 1;
RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
//
// Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.
//
ASSERT (RealPages > Pages);
Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, RealPages, &Memory);
if (EFI_ERROR(Status)) {
return NULL;
}
AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;
UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory);
if (UnalignedPages > 0) {
//
// Free first unaligned page(s).
//
Status = gBS->FreePages (Memory, UnalignedPages);
ASSERT_EFI_ERROR(Status);
}
Memory = AlignedMemory + EFI_PAGES_TO_SIZE (Pages);
UnalignedPages = RealPages - Pages - UnalignedPages;
if (UnalignedPages > 0) {
//
// Free last unaligned page(s).
//
Status = gBS->FreePages (Memory, UnalignedPages);
ASSERT_EFI_ERROR(Status);
}
} else {
//
// Do not over-allocate pages in this case.
//
Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);
if (EFI_ERROR(Status)) {
return NULL;
}
AlignedMemory = (UINTN) Memory;
}
return (VOID *) AlignedMemory;
Status = PhaseFreePages ((UINTN)Buffer, Pages);
ASSERT_EFI_ERROR (Status);
}
/**
@ -300,17 +234,18 @@ AllocateAlignedPages (
{
VOID *Buffer;
Buffer = InternalAllocateAlignedPages (EfiBootServicesData, Pages, Alignment);
Buffer = InternalAllocateAlignedPages (gPhaseDefaultDataType, Pages, Alignment);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_PAGES,
EfiBootServicesData,
gPhaseDefaultDataType,
Buffer,
EFI_PAGES_TO_SIZE (Pages),
NULL
);
}
return Buffer;
}
@ -344,7 +279,7 @@ AllocateAlignedRuntimePages (
Buffer = InternalAllocateAlignedPages (EfiRuntimeServicesData, Pages, Alignment);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_RUNTIME_PAGES,
EfiRuntimeServicesData,
Buffer,
@ -352,6 +287,7 @@ AllocateAlignedRuntimePages (
NULL
);
}
return Buffer;
}
@ -385,7 +321,7 @@ AllocateAlignedReservedPages (
Buffer = InternalAllocateAlignedPages (EfiReservedMemoryType, Pages, Alignment);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_RESERVED_PAGES,
EfiReservedMemoryType,
Buffer,
@ -393,9 +329,20 @@ AllocateAlignedReservedPages (
NULL
);
}
return Buffer;
}
VOID *
EFIAPI
AllocateAlignedCodePages (
IN UINTN Pages,
IN UINTN Alignment
)
{
return InternalAllocateAlignedPages (gPhaseDefaultCodeType, Pages, Alignment);
}
/**
Frees one or more 4KB pages that were previously allocated with one of the aligned page
allocation functions in the Memory Allocation Library.
@ -409,7 +356,7 @@ AllocateAlignedReservedPages (
Library, then ASSERT().
If Pages is zero, then ASSERT().
@param Buffer The pointer to the buffer of pages to free.
@param Buffer Pointer to the buffer of pages to free.
@param Pages The number of 4 KB pages to free.
**/
@ -420,40 +367,7 @@ FreeAlignedPages (
IN UINTN Pages
)
{
EFI_STATUS Status;
ASSERT (Pages != 0);
Status = gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) Buffer, Pages);
ASSERT_EFI_ERROR(Status);
}
/**
Allocates a buffer of a certain pool type.
Allocates the number bytes specified by AllocationSize of a certain pool type and returns a
pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is
returned. If there is not enough memory remaining to satisfy the request, then NULL is returned.
@param MemoryType The type of memory to allocate.
@param AllocationSize The number of bytes to allocate.
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
VOID *
InternalAllocatePool (
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN AllocationSize
)
{
EFI_STATUS Status;
VOID *Memory;
Status = gBS->AllocatePool (MemoryType, AllocationSize, &Memory);
if (EFI_ERROR(Status)) {
Memory = NULL;
}
return Memory;
InternalFreeAlignedPages (Buffer, Pages);
}
/**
@ -476,17 +390,18 @@ AllocatePool (
{
VOID *Buffer;
Buffer = InternalAllocatePool (EfiBootServicesData, AllocationSize);
Buffer = PhaseAllocatePool (gPhaseDefaultDataType, AllocationSize);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_POOL,
EfiBootServicesData,
gPhaseDefaultDataType,
Buffer,
AllocationSize,
NULL
);
}
return Buffer;
}
@ -510,10 +425,10 @@ AllocateRuntimePool (
{
VOID *Buffer;
Buffer = InternalAllocatePool (EfiRuntimeServicesData, AllocationSize);
Buffer = PhaseAllocatePool (EfiRuntimeServicesData, AllocationSize);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_POOL,
EfiRuntimeServicesData,
Buffer,
@ -521,6 +436,7 @@ AllocateRuntimePool (
NULL
);
}
return Buffer;
}
@ -544,10 +460,10 @@ AllocateReservedPool (
{
VOID *Buffer;
Buffer = InternalAllocatePool (EfiReservedMemoryType, AllocationSize);
Buffer = PhaseAllocatePool (EfiReservedMemoryType, AllocationSize);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_POOL,
EfiReservedMemoryType,
Buffer,
@ -555,6 +471,7 @@ AllocateReservedPool (
NULL
);
}
return Buffer;
}
@ -572,18 +489,20 @@ AllocateReservedPool (
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
STATIC
VOID *
InternalAllocateZeroPool(
InternalAllocateZeroPool (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN AllocationSize
)
{
VOID *Memory;
Memory = InternalAllocatePool (PoolType, AllocationSize);
Memory = PhaseAllocatePool (PoolType, AllocationSize);
if (Memory != NULL) {
Memory = ZeroMem (Memory, AllocationSize);
}
return Memory;
}
@ -602,23 +521,24 @@ InternalAllocateZeroPool(
**/
VOID *
EFIAPI
AllocateZeroPool(
AllocateZeroPool (
IN UINTN AllocationSize
)
{
VOID *Buffer;
Buffer = InternalAllocateZeroPool(EfiBootServicesData, AllocationSize);
Buffer = InternalAllocateZeroPool (gPhaseDefaultDataType, AllocationSize);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ZERO_POOL,
EfiBootServicesData,
gPhaseDefaultDataType,
Buffer,
AllocationSize,
NULL
);
}
return Buffer;
}
@ -643,10 +563,10 @@ AllocateRuntimeZeroPool (
{
VOID *Buffer;
Buffer = InternalAllocateZeroPool(EfiRuntimeServicesData, AllocationSize);
Buffer = InternalAllocateZeroPool (EfiRuntimeServicesData, AllocationSize);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_ZERO_POOL,
EfiRuntimeServicesData,
Buffer,
@ -654,6 +574,7 @@ AllocateRuntimeZeroPool (
NULL
);
}
return Buffer;
}
@ -678,10 +599,10 @@ AllocateReservedZeroPool (
{
VOID *Buffer;
Buffer = InternalAllocateZeroPool(EfiReservedMemoryType, AllocationSize);
Buffer = InternalAllocateZeroPool (EfiReservedMemoryType, AllocationSize);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_ZERO_POOL,
EfiReservedMemoryType,
Buffer,
@ -689,6 +610,7 @@ AllocateReservedZeroPool (
NULL
);
}
return Buffer;
}
@ -709,8 +631,9 @@ AllocateReservedZeroPool (
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
STATIC
VOID *
InternalAllocateCopyPool(
InternalAllocateCopyPool (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN AllocationSize,
IN CONST VOID *Buffer
@ -719,12 +642,13 @@ InternalAllocateCopyPool(
VOID *Memory;
ASSERT (Buffer != NULL);
ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1));
ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN)Buffer + 1));
Memory = InternalAllocatePool (PoolType, AllocationSize);
Memory = PhaseAllocatePool (PoolType, AllocationSize);
if (Memory != NULL) {
Memory = CopyMem(Memory, Buffer, AllocationSize);
Memory = CopyMem (Memory, Buffer, AllocationSize);
}
return Memory;
}
@ -747,24 +671,25 @@ InternalAllocateCopyPool(
**/
VOID *
EFIAPI
AllocateCopyPool(
AllocateCopyPool (
IN UINTN AllocationSize,
IN CONST VOID *Buffer
)
{
VOID *NewBuffer;
NewBuffer = InternalAllocateCopyPool(EfiBootServicesData, AllocationSize, Buffer);
NewBuffer = InternalAllocateCopyPool (gPhaseDefaultDataType, AllocationSize, Buffer);
if (NewBuffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_COPY_POOL,
EfiBootServicesData,
gPhaseDefaultDataType,
NewBuffer,
AllocationSize,
NULL
);
}
return NewBuffer;
}
@ -794,10 +719,10 @@ AllocateRuntimeCopyPool (
{
VOID *NewBuffer;
NewBuffer = InternalAllocateCopyPool(EfiRuntimeServicesData, AllocationSize, Buffer);
NewBuffer = InternalAllocateCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer);
if (NewBuffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_COPY_POOL,
EfiRuntimeServicesData,
NewBuffer,
@ -805,6 +730,7 @@ AllocateRuntimeCopyPool (
NULL
);
}
return NewBuffer;
}
@ -834,10 +760,10 @@ AllocateReservedCopyPool (
{
VOID *NewBuffer;
NewBuffer = InternalAllocateCopyPool(EfiReservedMemoryType, AllocationSize, Buffer);
NewBuffer = InternalAllocateCopyPool (EfiReservedMemoryType, AllocationSize, Buffer);
if (NewBuffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_COPY_POOL,
EfiRuntimeServicesData,
NewBuffer,
@ -845,6 +771,7 @@ AllocateReservedCopyPool (
NULL
);
}
return NewBuffer;
}
@ -870,6 +797,7 @@ AllocateReservedCopyPool (
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
STATIC
VOID *
InternalReallocatePool (
IN EFI_MEMORY_TYPE PoolType,
@ -880,11 +808,12 @@ InternalReallocatePool (
{
VOID *NewBuffer;
NewBuffer = InternalAllocateZeroPool(PoolType, NewSize);
if (NewBuffer != NULL && OldBuffer != NULL) {
CopyMem(NewBuffer, OldBuffer, MIN (OldSize, NewSize));
FreePool(OldBuffer);
NewBuffer = InternalAllocateZeroPool (PoolType, NewSize);
if ((NewBuffer != NULL) && (OldBuffer != NULL)) {
CopyMem (NewBuffer, OldBuffer, MIN (OldSize, NewSize));
FreePool (OldBuffer);
}
return NewBuffer;
}
@ -919,17 +848,18 @@ ReallocatePool (
{
VOID *Buffer;
Buffer = InternalReallocatePool (EfiBootServicesData, OldSize, NewSize, OldBuffer);
Buffer = InternalReallocatePool (gPhaseDefaultDataType, OldSize, NewSize, OldBuffer);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_REALLOCATE_POOL,
EfiBootServicesData,
gPhaseDefaultDataType,
Buffer,
NewSize,
NULL
);
}
return Buffer;
}
@ -967,7 +897,7 @@ ReallocateRuntimePool (
Buffer = InternalReallocatePool (EfiRuntimeServicesData, OldSize, NewSize, OldBuffer);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_REALLOCATE_RUNTIME_POOL,
EfiRuntimeServicesData,
Buffer,
@ -975,6 +905,7 @@ ReallocateRuntimePool (
NULL
);
}
return Buffer;
}
@ -1012,7 +943,7 @@ ReallocateReservedPool (
Buffer = InternalReallocatePool (EfiReservedMemoryType, OldSize, NewSize, OldBuffer);
if (Buffer != NULL) {
MemoryProfileLibRecord (
(PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS(0),
(PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
MEMORY_PROFILE_ACTION_LIB_REALLOCATE_RESERVED_POOL,
EfiReservedMemoryType,
Buffer,
@ -1020,6 +951,7 @@ ReallocateReservedPool (
NULL
);
}
return Buffer;
}
@ -1034,18 +966,14 @@ ReallocateReservedPool (
If Buffer was not allocated with a pool allocation function in the Memory Allocation Library,
then ASSERT().
@param Buffer The pointer to the buffer to free.
@param Buffer Pointer to the buffer to free.
**/
VOID
EFIAPI
FreePool(
IN VOID *Buffer
FreePool (
IN VOID *Buffer
)
{
EFI_STATUS Status;
Status = gBS->FreePool(Buffer);
ASSERT_EFI_ERROR(Status);
PhaseFreePool (Buffer);
}

View File

@ -0,0 +1,39 @@
## @file
# Common code for the Memory Allocation Library based on
# PhaseMemoryAllocationLib.
#
# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = CommonMemoryAllocationLib
MODULE_UNI_FILE = CommonMemoryAllocationLib.uni
FILE_GUID = 020172f8-f96d-479b-9f29-c4d2f204573d
MODULE_TYPE = BASE
VERSION_STRING = 1.0
LIBRARY_CLASS = CommonMemoryAllocationLib
#
# VALID_ARCHITECTURES = IA32 X64 EBC
#
[Sources]
AlignedPages.h
AlignedPages.c
CommonMemoryAllocationLib.c
CommonMemoryAllocationLibEx.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
DebugLib
BaseMemoryLib
MemoryProfileLib

View File

@ -0,0 +1,15 @@
// /** @file
// Common code for the Memory Allocation Library based on
// PhaseMemoryAllocationLib.
//
// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Common code for the Memory Allocation Library based on PhaseMemoryAllocationLib"
#string STR_MODULE_DESCRIPTION #language en-US "Common code for the Memory Allocation Library based on PhaseMemoryAllocationLib."

View File

@ -0,0 +1,150 @@
/** @file
Implementation of MemoryAllocationLibEx based on PhaseMemoryAllocationLib.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2023, Marvin Häuser. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiDxe.h>
#include <Library/MemoryAllocationLibEx.h>
#include <Library/PhaseMemoryAllocationLib.h>
#include <Library/DebugLib.h>
/**
Allocates one or more 4KB pages of a certain memory type.
Allocates the number of 4KB pages of a certain memory type and returns a pointer
to the allocated buffer. The buffer returned is aligned on a 4KB boundary.
@param Type The type of allocation to perform.
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@param Memory The pointer to a physical address. On input, the
way in which the address is used depends on the
value of Type.
@retval EFI_SUCCESS The requested pages were allocated.
@retval EFI_OUT_OF_RESOURCES The pages could not be allocated.
@retval EFI_NOT_FOUND The requested pages could not be found.
**/
EFI_STATUS
EFIAPI
AllocatePagesEx (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN OUT EFI_PHYSICAL_ADDRESS *Memory
)
{
return PhaseAllocatePages (Type, MemoryType, Pages, Memory);
}
/**
Allocates one or more 4KB pages of a certain memory type at a specified alignment.
Allocates the number of 4KB pages specified by Pages of a certain memory type with an alignment
specified by Alignment.
If Alignment is not a power of two and Alignment is not zero, then ASSERT().
If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
@param Type The type of allocation to perform.
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@param Alignment The requested alignment of the allocation. Must be a power of two.
If Alignment is zero, then byte alignment is used.
@param Memory The pointer to a physical address. On input, the
way in which the address is used depends on the
value of Type.
@retval EFI_SUCCESS The requested pages were allocated.
@retval EFI_OUT_OF_RESOURCES The pages could not be allocated.
@retval EFI_NOT_FOUND The requested pages could not be found.
**/
EFI_STATUS
EFIAPI
AllocateAlignedPagesEx (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN UINTN Alignment,
IN OUT EFI_PHYSICAL_ADDRESS *Memory
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Address;
EFI_PHYSICAL_ADDRESS AlignedMemory;
UINTN AlignmentMask;
UINTN UnalignedPages;
UINTN RealPages;
//
// Alignment must be a power of two or zero.
//
ASSERT ((Alignment & (Alignment - 1)) == 0);
//
// Reserving a specific address does not require guaranteeing alignment
// constraints.
//
ASSERT (Type != AllocateAddress);
if (Pages == 0) {
return EFI_INVALID_PARAMETER;
}
if (Alignment > EFI_PAGE_SIZE) {
//
// Calculate the total number of pages since alignment is larger than page size.
//
AlignmentMask = Alignment - 1;
RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
//
// Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.
//
ASSERT (RealPages > Pages);
Status = AllocatePagesEx (Type, MemoryType, RealPages, Memory);
if (EFI_ERROR (Status)) {
return Status;
}
Address = *Memory;
AlignedMemory = (Address + AlignmentMask) & ~(EFI_PHYSICAL_ADDRESS)AlignmentMask;
UnalignedPages = EFI_SIZE_TO_PAGES ((UINTN)(AlignedMemory - Address));
if (UnalignedPages > 0) {
//
// Free first unaligned page(s).
//
Status = PhaseFreePages (Address, UnalignedPages);
ASSERT_EFI_ERROR (Status);
}
Address = AlignedMemory + EFI_PAGES_TO_SIZE (Pages);
UnalignedPages = RealPages - Pages - UnalignedPages;
if (UnalignedPages > 0) {
//
// Free last unaligned page(s).
//
Status = PhaseFreePages (Address, UnalignedPages);
ASSERT_EFI_ERROR (Status);
}
*Memory = AlignedMemory;
} else {
//
// Do not over-allocate pages in this case.
//
Status = AllocatePagesEx (Type, MemoryType, Pages, Memory);
if (EFI_ERROR (Status)) {
return Status;
}
}
return EFI_SUCCESS;
}

View File

@ -29,7 +29,6 @@
[Sources]
MemoryAllocationLib.c
DxeCoreMemoryAllocationServices.h
DxeCoreMemoryProfileLibNull.c
[Packages]
MdePkg/MdePkg.dec
@ -38,3 +37,5 @@
[LibraryClasses]
DebugLib
BaseMemoryLib
MemoryProfileLib
CommonMemoryAllocationLib

View File

@ -1,7 +1,7 @@
## @file
# Memory Allocation/Profile Library instance dedicated to DXE Core.
# The implementation borrows the DxeCore Memory Allocation/profile services as the primitive
# for memory allocation/profile instead of using UEFI boot services or memory profile protocol in an indirect way.
# Memory Profile Library instance dedicated to DXE Core.
# The implementation borrows the DxeCore Memory profile services as the primitive
# for memory profile instead of using UEFI boot services or memory profile protocol in an indirect way.
# It is assumed that this library instance must be linked with DxeCore in this package.
#
# Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
@ -18,7 +18,6 @@
FILE_GUID = 7ADD7147-74E8-4583-BE34-B6BC45353BB5
MODULE_TYPE = DXE_CORE
VERSION_STRING = 1.0
LIBRARY_CLASS = MemoryAllocationLib|DXE_CORE
LIBRARY_CLASS = MemoryProfileLib|DXE_CORE
#
@ -28,8 +27,6 @@
#
[Sources]
MemoryAllocationLib.c
DxeCoreMemoryAllocationServices.h
DxeCoreMemoryProfileLib.c
DxeCoreMemoryProfileServices.h
@ -37,7 +34,3 @@
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
DebugLib
BaseMemoryLib

View File

@ -1,8 +1,8 @@
// /** @file
// Memory Allocation/Profile Library instance dedicated to DXE Core.
// Memory Profile Library instance dedicated to DXE Core.
//
// The implementation borrows the DxeCore Memory Allocation/Profile services as the primitive
// for memory allocation/profile instead of using UEFI boot services or memory profile protocol in an indirect way.
// The implementation borrows the DxeCore Memory Profile services as the primitive
// for memory profile instead of using UEFI boot services or memory profile protocol in an indirect way.
// It is assumed that this library instance must be linked with DxeCore in this package.
//
// Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>
@ -12,7 +12,7 @@
// **/
#string STR_MODULE_ABSTRACT #language en-US "Memory Allocation/Profile Library instance dedicated to DXE Core"
#string STR_MODULE_ABSTRACT #language en-US "Memory Profile Library instance dedicated to DXE Core"
#string STR_MODULE_DESCRIPTION #language en-US "The implementation borrows the DxeCore Memory Allocation/Profile services as the primitive for memory allocation/profile instead of using UEFI boot services or memory profile protocol in an indirect way. It is assumed that this library instance must be linked with DxeCore in this package."
#string STR_MODULE_DESCRIPTION #language en-US "The implementation borrows the DxeCore Memory Profile services as the primitive for memory profile instead of using UEFI boot services or memory profile protocol in an indirect way. It is assumed that this library instance must be linked with DxeCore in this package."

View File

@ -12,7 +12,6 @@
#ifndef _DXE_CORE_MEMORY_ALLOCATION_SERVICES_H_
#define _DXE_CORE_MEMORY_ALLOCATION_SERVICES_H_
/**
Allocates pages from the memory map.
@ -34,14 +33,12 @@
EFI_STATUS
EFIAPI
CoreAllocatePages (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN NumberOfPages,
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN NumberOfPages,
IN OUT EFI_PHYSICAL_ADDRESS *Memory
);
/**
Frees previous allocated pages.
@ -56,11 +53,10 @@ CoreAllocatePages (
EFI_STATUS
EFIAPI
CoreFreePages (
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN NumberOfPages
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN NumberOfPages
);
/**
Allocate pool of a particular type.
@ -93,8 +89,8 @@ CoreAllocatePool (
**/
EFI_STATUS
EFIAPI
CoreFreePool(
IN VOID *Buffer
CoreFreePool (
IN VOID *Buffer
);
#endif

View File

@ -6,7 +6,6 @@
**/
#include <PiDxe.h>
#include <Guid/MemoryProfile.h>
@ -38,14 +37,13 @@
EFI_STATUS
EFIAPI
MemoryProfileLibRecord (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
)
{
return CoreUpdateProfile (CallerAddress, Action, MemoryType, Size, Buffer, ActionString);
}

View File

@ -1,42 +0,0 @@
## @file
# Memory Allocation Library instance dedicated to SMM Core.
# The implementation borrows the SMM Core Memory Allocation services as the primitive
# for memory allocation instead of using SMM System Table services in an indirect way.
# It is assumed that this library instance must be linked with SMM Cre in this package.
#
# Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = PiSmmCoreMemoryAllocationLib
MODULE_UNI_FILE = PiSmmCoreMemoryAllocationLib.uni
FILE_GUID = B618E089-9ABA-4d97-AE80-57B5BCCDA51D
MODULE_TYPE = SMM_CORE
VERSION_STRING = 1.0
PI_SPECIFICATION_VERSION = 0x0001000A
LIBRARY_CLASS = MemoryAllocationLib|SMM_CORE
CONSTRUCTOR = PiSmmCoreMemoryAllocationLibConstructor
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64
#
[Sources]
MemoryAllocationLib.c
PiSmmCoreMemoryAllocationServices.h
PiSmmCoreMemoryProfileLibNull.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
DebugLib
BaseMemoryLib
UefiBootServicesTableLib

View File

@ -1,18 +0,0 @@
// /** @file
// Memory Allocation Library instance dedicated to SMM Core.
//
// The implementation borrows the SMM Core Memory Allocation services as the primitive
// for memory allocation instead of using SMM System Table services in an indirect way.
// It is assumed that this library instance must be linked with SMM Cre in this package.
//
// Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Memory Allocation Library instance dedicated to SMM Core"
#string STR_MODULE_DESCRIPTION #language en-US "The implementation borrows the SMM Core Memory Allocation services as the primitive for memory allocation instead of using SMM System Table services in an indirect way. This library is only intended to be linked with the SMM Core that resides in this same package."

View File

@ -1,49 +0,0 @@
## @file
# Memory Allocation/Profile Library instance dedicated to SMM Core.
# The implementation borrows the SMM Core Memory Allocation/Profile services as the primitive
# for memory allocation/profile instead of using SMM System Table servces or SMM memory profile protocol in an indirect way.
# It is assumed that this library instance must be linked with SMM Cre in this package.
#
# Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = PiSmmCoreMemoryAllocationProfileLib
MODULE_UNI_FILE = PiSmmCoreMemoryAllocationProfileLib.uni
FILE_GUID = D55E42AD-3E63-4536-8281-82C0F1098C5E
MODULE_TYPE = SMM_CORE
VERSION_STRING = 1.0
PI_SPECIFICATION_VERSION = 0x0001000A
LIBRARY_CLASS = MemoryAllocationLib|SMM_CORE
CONSTRUCTOR = PiSmmCoreMemoryAllocationLibConstructor
LIBRARY_CLASS = MemoryProfileLib|SMM_CORE
CONSTRUCTOR = PiSmmCoreMemoryProfileLibConstructor
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64
#
[Sources]
MemoryAllocationLib.c
PiSmmCoreMemoryAllocationServices.h
PiSmmCoreMemoryProfileLib.c
PiSmmCoreMemoryProfileServices.h
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
DebugLib
BaseMemoryLib
UefiBootServicesTableLib
[Guids]
gEdkiiMemoryProfileGuid ## SOMETIMES_CONSUMES ## GUID # Locate protocol

View File

@ -1,18 +0,0 @@
// /** @file
// Memory Allocation/Profile Library instance dedicated to SMM Core.
//
// The implementation borrows the SMM Core Memory Allocation/Profile services as the primitive
// for memory allocation/profile instead of using SMM System Table servces or SMM memory profile protocol in an indirect way.
// It is assumed that this library instance must be linked with SMM Cre in this package.
//
// Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Memory Allocation/Profile Library instance dedicated to SMM Core"
#string STR_MODULE_DESCRIPTION #language en-US "The implementation borrows the SMM Core Memory Allocation/Profile services as the primitive for memory allocation/profile instead of using SMM System Table services or SMM memory profile protocol in an indirect way. This library is only intended to be linked with the SMM Core that resides in this same package."

View File

@ -1,185 +0,0 @@
/** @file
Contains function prototypes for Memory Services in the SMM Core.
This header file borrows the PiSmmCore Memory Allocation services as the primitive
for memory allocation.
Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef _PI_SMM_CORE_MEMORY_ALLOCATION_SERVICES_H_
#define _PI_SMM_CORE_MEMORY_ALLOCATION_SERVICES_H_
//
// It should be aligned with the definition in PiSmmCore.
//
typedef struct {
UINTN Signature;
///
/// The ImageHandle passed into the entry point of the SMM IPL. This ImageHandle
/// is used by the SMM Core to fill in the ParentImageHandle field of the Loaded
/// Image Protocol for each SMM Driver that is dispatched by the SMM Core.
///
EFI_HANDLE SmmIplImageHandle;
///
/// The number of SMRAM ranges passed from the SMM IPL to the SMM Core. The SMM
/// Core uses these ranges of SMRAM to initialize the SMM Core memory manager.
///
UINTN SmramRangeCount;
///
/// A table of SMRAM ranges passed from the SMM IPL to the SMM Core. The SMM
/// Core uses these ranges of SMRAM to initialize the SMM Core memory manager.
///
EFI_SMRAM_DESCRIPTOR *SmramRanges;
///
/// The SMM Foundation Entry Point. The SMM Core fills in this field when the
/// SMM Core is initialized. The SMM IPL is responsbile for registering this entry
/// point with the SMM Configuration Protocol. The SMM Configuration Protocol may
/// not be available at the time the SMM IPL and SMM Core are started, so the SMM IPL
/// sets up a protocol notification on the SMM Configuration Protocol and registers
/// the SMM Foundation Entry Point as soon as the SMM Configuration Protocol is
/// available.
///
EFI_SMM_ENTRY_POINT SmmEntryPoint;
///
/// Boolean flag set to TRUE while an SMI is being processed by the SMM Core.
///
BOOLEAN SmmEntryPointRegistered;
///
/// Boolean flag set to TRUE while an SMI is being processed by the SMM Core.
///
BOOLEAN InSmm;
///
/// This field is set by the SMM Core then the SMM Core is initialized. This field is
/// used by the SMM Base 2 Protocol and SMM Communication Protocol implementations in
/// the SMM IPL.
///
EFI_SMM_SYSTEM_TABLE2 *Smst;
///
/// This field is used by the SMM Communicatioon Protocol to pass a buffer into
/// a software SMI handler and for the software SMI handler to pass a buffer back to
/// the caller of the SMM Communication Protocol.
///
VOID *CommunicationBuffer;
///
/// This field is used by the SMM Communicatioon Protocol to pass the size of a buffer,
/// in bytes, into a software SMI handler and for the software SMI handler to pass the
/// size, in bytes, of a buffer back to the caller of the SMM Communication Protocol.
///
UINTN BufferSize;
///
/// This field is used by the SMM Communication Protocol to pass the return status from
/// a software SMI handler back to the caller of the SMM Communication Protocol.
///
EFI_STATUS ReturnStatus;
EFI_PHYSICAL_ADDRESS PiSmmCoreImageBase;
UINT64 PiSmmCoreImageSize;
EFI_PHYSICAL_ADDRESS PiSmmCoreEntryPoint;
} SMM_CORE_PRIVATE_DATA;
/**
Called to initialize the memory service.
@param SmramRangeCount Number of SMRAM Regions
@param SmramRanges Pointer to SMRAM Descriptors
**/
VOID
SmmInitializeMemoryServices (
IN UINTN SmramRangeCount,
IN EFI_SMRAM_DESCRIPTOR *SmramRanges
);
/**
Allocates pages from the memory map.
@param Type The type of allocation to perform
@param MemoryType The type of memory to turn the allocated pages
into
@param NumberOfPages The number of pages to allocate
@param Memory A pointer to receive the base allocated memory
address
@retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec.
@retval EFI_NOT_FOUND Could not allocate pages match the requirement.
@retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
@retval EFI_SUCCESS Pages successfully allocated.
**/
EFI_STATUS
EFIAPI
SmmAllocatePages (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN NumberOfPages,
OUT EFI_PHYSICAL_ADDRESS *Memory
);
/**
Frees previous allocated pages.
@param Memory Base address of memory being freed
@param NumberOfPages The number of pages to free
@retval EFI_NOT_FOUND Could not find the entry that covers the range
@retval EFI_INVALID_PARAMETER Address not aligned
@return EFI_SUCCESS Pages successfully freed.
**/
EFI_STATUS
EFIAPI
SmmFreePages (
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN NumberOfPages
);
/**
Allocate pool of a particular type.
@param PoolType Type of pool to allocate
@param Size The amount of pool to allocate
@param Buffer The address to return a pointer to the allocated
pool
@retval EFI_INVALID_PARAMETER PoolType not valid
@retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
@retval EFI_SUCCESS Pool successfully allocated.
**/
EFI_STATUS
EFIAPI
SmmAllocatePool (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN Size,
OUT VOID **Buffer
);
/**
Frees pool.
@param Buffer The allocated pool entry to free
@retval EFI_INVALID_PARAMETER Buffer is not a valid value.
@retval EFI_SUCCESS Pool successfully freed.
**/
EFI_STATUS
EFIAPI
SmmFreePool(
IN VOID *Buffer
);
#endif

View File

@ -1,117 +0,0 @@
/** @file
Support routines for memory profile for PiSmmCore.
Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiSmm.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Guid/MemoryProfile.h>
#include "PiSmmCoreMemoryProfileServices.h"
EDKII_MEMORY_PROFILE_PROTOCOL *mLibProfileProtocol;
/**
Check whether the start address of buffer is within any of the SMRAM ranges.
@param[in] Buffer The pointer to the buffer to be checked.
@retval TRUE The buffer is in SMRAM ranges.
@retval FALSE The buffer is out of SMRAM ranges.
**/
BOOLEAN
EFIAPI
BufferInSmram (
IN VOID *Buffer
);
/**
The constructor function initializes memory profile for SMM phase.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
PiSmmCoreMemoryProfileLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Locate Profile Protocol
//
Status = gBS->LocateProtocol (
&gEdkiiMemoryProfileGuid,
NULL,
(VOID **)&mLibProfileProtocol
);
if (EFI_ERROR(Status)) {
mLibProfileProtocol = NULL;
}
return EFI_SUCCESS;
}
/**
Record memory profile of multilevel caller.
@param[in] CallerAddress Address of caller.
@param[in] Action Memory profile action.
@param[in] MemoryType Memory type.
EfiMaxMemoryType means the MemoryType is unknown.
@param[in] Buffer Buffer address.
@param[in] Size Buffer size.
@param[in] ActionString String for memory profile action.
Only needed for user defined allocate action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required,
or memory profile for the memory type is not required.
@return EFI_ACCESS_DENIED It is during memory profile data getting.
@return EFI_ABORTED Memory profile recording is not enabled.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
EFIAPI
MemoryProfileLibRecord (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
)
{
if (BufferInSmram (Buffer)) {
return SmmCoreUpdateProfile (CallerAddress, Action, MemoryType, Size, Buffer, ActionString);
} else {
if (mLibProfileProtocol == NULL) {
return EFI_UNSUPPORTED;
}
return mLibProfileProtocol->Record (
mLibProfileProtocol,
CallerAddress,
Action,
MemoryType,
Buffer,
Size,
ActionString
);
}
}

View File

@ -1,48 +0,0 @@
/** @file
Null routines for memory profile for PiSmmCore.
Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiSmm.h>
#include <Guid/MemoryProfile.h>
/**
Record memory profile of multilevel caller.
@param[in] CallerAddress Address of caller.
@param[in] Action Memory profile action.
@param[in] MemoryType Memory type.
EfiMaxMemoryType means the MemoryType is unknown.
@param[in] Buffer Buffer address.
@param[in] Size Buffer size.
@param[in] ActionString String for memory profile action.
Only needed for user defined allocate action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required,
or memory profile for the memory type is not required.
@return EFI_ACCESS_DENIED It is during memory profile data getting.
@return EFI_ABORTED Memory profile recording is not enabled.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
EFIAPI
MemoryProfileLibRecord (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
)
{
return EFI_UNSUPPORTED;
}

View File

@ -1,48 +0,0 @@
/** @file
Contains function prototypes for Memory Profile Services in the SMM Core.
This header file borrows the PiSmmCore Memory Profile services as the primitive
for memory profile.
Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef _PI_SMM_CORE_MEMORY_PROFILE_SERVICES_H_
#define _PI_SMM_CORE_MEMORY_PROFILE_SERVICES_H_
/**
Update SMRAM profile information.
@param CallerAddress Address of caller who call Allocate or Free.
@param Action This Allocate or Free action.
@param MemoryType Memory type.
EfiMaxMemoryType means the MemoryType is unknown.
@param Size Buffer size.
@param Buffer Buffer address.
@param ActionString String for memory profile action.
Only needed for user defined allocate action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required,
or memory profile for the memory type is not required.
@return EFI_ACCESS_DENIED It is during memory profile data getting.
@return EFI_ABORTED Memory profile recording is not enabled.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
EFIAPI
SmmCoreUpdateProfile (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType, // Valid for AllocatePages/AllocatePool
IN UINTN Size, // Valid for AllocatePages/FreePages/AllocatePool
IN VOID *Buffer,
IN CHAR8 *ActionString OPTIONAL
);
#endif

View File

@ -1,57 +0,0 @@
## @file
# Instance of Memory Allocation Library using SMM Services Table,
# with memory profile support.
#
# Memory Allocation Library that uses services from the SMM Services Table to
# allocate and free memory, with memory profile support.
#
# The implementation of this instance is copied from UefiMemoryAllocationLib
# in MdePkg and updated to support both MemoryAllocationLib and MemoryProfileLib.
#
# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = SmmMemoryAllocationProfileLib
MODULE_UNI_FILE = SmmMemoryAllocationProfileLib.uni
FILE_GUID = DC50729F-8633-47ab-8FD3-6939688CEE4C
MODULE_TYPE = DXE_SMM_DRIVER
VERSION_STRING = 1.0
PI_SPECIFICATION_VERSION = 0x0001000A
LIBRARY_CLASS = MemoryAllocationLib|DXE_SMM_DRIVER
CONSTRUCTOR = SmmMemoryAllocationLibConstructor
DESTRUCTOR = SmmMemoryAllocationLibDestructor
LIBRARY_CLASS = MemoryProfileLib|DXE_SMM_DRIVER
CONSTRUCTOR = SmmMemoryProfileLibConstructor
#
# VALID_ARCHITECTURES = IA32 X64
#
[Sources]
MemoryAllocationLib.c
SmmMemoryProfileLib.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
DebugLib
BaseMemoryLib
SmmServicesTableLib
UefiBootServicesTableLib
[Protocols]
gEfiSmmAccess2ProtocolGuid ## CONSUMES
[Guids]
gEdkiiMemoryProfileGuid ## SOMETIMES_CONSUMES ## GUID # Locate protocol
gEdkiiSmmMemoryProfileGuid ## SOMETIMES_CONSUMES ## GUID # Locate protocol
[Depex]
gEfiSmmAccess2ProtocolGuid

View File

@ -1,18 +0,0 @@
// /** @file
// Instance of Memory Allocation Library using SMM Services Table,
// with memory profile support.
//
// Memory Allocation Library that uses services from the SMM Services Table to
// allocate and free memory, with memory profile support.
//
// Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Instance of Memory Allocation Library using SMM Services Table, with memory profile support"
#string STR_MODULE_DESCRIPTION #language en-US "This Memory Allocation Library uses services from the SMM Services Table to allocate and free memory, with memory profile support."

View File

@ -1,137 +0,0 @@
/** @file
Support routines for memory profile for Smm phase drivers.
Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiSmm.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/SmmServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Guid/MemoryProfile.h>
EDKII_MEMORY_PROFILE_PROTOCOL *mLibProfileProtocol;
EDKII_SMM_MEMORY_PROFILE_PROTOCOL *mLibSmmProfileProtocol;
/**
Check whether the start address of buffer is within any of the SMRAM ranges.
@param[in] Buffer The pointer to the buffer to be checked.
@retval TRUE The buffer is in SMRAM ranges.
@retval FALSE The buffer is out of SMRAM ranges.
**/
BOOLEAN
EFIAPI
BufferInSmram (
IN VOID *Buffer
);
/**
The constructor function initializes memory profile for SMM phase.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
SmmMemoryProfileLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Locate Profile Protocol
//
Status = gBS->LocateProtocol (
&gEdkiiMemoryProfileGuid,
NULL,
(VOID **)&mLibProfileProtocol
);
if (EFI_ERROR(Status)) {
mLibProfileProtocol = NULL;
}
Status = gSmst->SmmLocateProtocol (
&gEdkiiSmmMemoryProfileGuid,
NULL,
(VOID **)&mLibSmmProfileProtocol
);
if (EFI_ERROR(Status)) {
mLibSmmProfileProtocol = NULL;
}
return EFI_SUCCESS;
}
/**
Record memory profile of multilevel caller.
@param[in] CallerAddress Address of caller.
@param[in] Action Memory profile action.
@param[in] MemoryType Memory type.
EfiMaxMemoryType means the MemoryType is unknown.
@param[in] Buffer Buffer address.
@param[in] Size Buffer size.
@param[in] ActionString String for memory profile action.
Only needed for user defined allocate action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required,
or memory profile for the memory type is not required.
@return EFI_ACCESS_DENIED It is during memory profile data getting.
@return EFI_ABORTED Memory profile recording is not enabled.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
EFIAPI
MemoryProfileLibRecord (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
)
{
if (BufferInSmram (Buffer)) {
if (mLibSmmProfileProtocol == NULL) {
return EFI_UNSUPPORTED;
}
return mLibSmmProfileProtocol->Record (
mLibSmmProfileProtocol,
CallerAddress,
Action,
MemoryType,
Buffer,
Size,
ActionString
);
} else {
if (mLibProfileProtocol == NULL) {
return EFI_UNSUPPORTED;
}
return mLibProfileProtocol->Record (
mLibProfileProtocol,
CallerAddress,
Action,
MemoryType,
Buffer,
Size,
ActionString
);
}
}

View File

@ -1,96 +0,0 @@
/** @file
Support routines for memory profile for Dxe phase drivers.
Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Guid/MemoryProfile.h>
EDKII_MEMORY_PROFILE_PROTOCOL *mLibProfileProtocol;
/**
The constructor function initializes memory profile for DXE phase.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
MemoryProfileLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = gBS->LocateProtocol (
&gEdkiiMemoryProfileGuid,
NULL,
(VOID **) &mLibProfileProtocol
);
if (EFI_ERROR(Status)) {
mLibProfileProtocol = NULL;
}
return EFI_SUCCESS;
}
/**
Record memory profile of multilevel caller.
@param[in] CallerAddress Address of caller.
@param[in] Action Memory profile action.
@param[in] MemoryType Memory type.
EfiMaxMemoryType means the MemoryType is unknown.
@param[in] Buffer Buffer address.
@param[in] Size Buffer size.
@param[in] ActionString String for memory profile action.
Only needed for user defined allocate action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required,
or memory profile for the memory type is not required.
@return EFI_ACCESS_DENIED It is during memory profile data getting.
@return EFI_ABORTED Memory profile recording is not enabled.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
EFIAPI
MemoryProfileLibRecord (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
)
{
if (mLibProfileProtocol == NULL) {
return EFI_UNSUPPORTED;
}
return mLibProfileProtocol->Record (
mLibProfileProtocol,
CallerAddress,
Action,
MemoryType,
Buffer,
Size,
ActionString
);
}

View File

@ -1,48 +0,0 @@
## @file
# Instance of Memory Allocation Library using EFI Boot Services,
# with memory profile support.
#
# Memory Allocation Library that uses EFI Boot Services to allocate
# and free memory, with memory profile support.
#
# The implementation of this instance is copied from UefiMemoryAllocationLib
# in MdePkg and updated to support both MemoryAllocationLib and MemoryProfileLib.
#
# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = UefiMemoryAllocationProfileLib
MODULE_UNI_FILE = UefiMemoryAllocationProfileLib.uni
FILE_GUID = 9E8A380A-231E-41E4-AD40-5E706196B853
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
LIBRARY_CLASS = MemoryAllocationLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
LIBRARY_CLASS = MemoryProfileLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
CONSTRUCTOR = MemoryProfileLibConstructor
#
# VALID_ARCHITECTURES = IA32 X64 EBC
#
[Sources]
MemoryAllocationLib.c
DxeMemoryProfileLib.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
DebugLib
BaseMemoryLib
UefiBootServicesTableLib
[Guids]
gEdkiiMemoryProfileGuid ## SOMETIMES_CONSUMES ## GUID # Locate protocol

View File

@ -1,18 +0,0 @@
// /** @file
// Instance of Memory Allocation Library using EFI Boot Services,
// with memory profile support.
//
// Memory Allocation Library that uses EFI Boot Services to allocate
// and free memory, with memory profile support.
//
// Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Instance of Memory Allocation Library using EFI Boot Services, with memory profile support"
#string STR_MODULE_DESCRIPTION #language en-US "This Memory Allocation Library uses EFI Boot Services to allocate and free memory, with memory profile support."

View File

@ -0,0 +1,410 @@
/** @file -- VariablePolicyHelperLib.c
This library contains helper functions for marshalling and registering
new policies with the VariablePolicy infrastructure.
This library is currently written against VariablePolicy revision 0x00010000.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/VariablePolicy.h>
/**
This internal helper function populates the header structure,
all common fields, and takes care of fix-ups.
NOTE: Only use this internally. Assumes correctly-sized buffers.
@param[out] EntPtr Pointer to the buffer to be populated.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] LockPolicyType LockPolicyType for the VariablePolicy.
**/
STATIC
VOID
PopulateCommonData (
OUT VARIABLE_POLICY_ENTRY *EntPtr,
IN CONST EFI_GUID *Namespace,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN UINT8 LockPolicyType
)
{
EntPtr->Version = VARIABLE_POLICY_ENTRY_REVISION;
CopyGuid (&EntPtr->Namespace, Namespace);
EntPtr->MinSize = MinSize;
EntPtr->MaxSize = MaxSize;
EntPtr->AttributesMustHave = AttributesMustHave;
EntPtr->AttributesCantHave = AttributesCantHave;
EntPtr->LockPolicyType = LockPolicyType;
// NOTE: As a heler, fix up MaxSize for compatibility with the old model.
if (EntPtr->MaxSize == 0) {
EntPtr->MaxSize = VARIABLE_POLICY_NO_MAX_SIZE;
}
return;
}
/**
This helper function will allocate and populate a new VariablePolicy
structure for a policy that does not contain any sub-structures (such as
VARIABLE_LOCK_ON_VAR_STATE_POLICY).
NOTE: Caller will need to free structure once finished.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name.
Otherwise, will create a policy that targets an entire namespace.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] LockPolicyType LockPolicyType for the VariablePolicy.
@param[out] NewEntry If successful, will be set to a pointer to the allocated buffer containing the
new policy.
@retval EFI_SUCCESS Operation completed successfully and structure is populated.
@retval EFI_INVALID_PARAMETER Namespace is NULL.
@retval EFI_INVALID_PARAMETER LockPolicyType is invalid for a basic structure.
@retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in UINT16 size.
@retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space for structure.
**/
EFI_STATUS
EFIAPI
CreateBasicVariablePolicy (
IN CONST EFI_GUID *Namespace,
IN CONST CHAR16 *Name OPTIONAL,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN UINT8 LockPolicyType,
OUT VARIABLE_POLICY_ENTRY **NewEntry
)
{
UINTN TotalSize;
UINTN NameSize;
VARIABLE_POLICY_ENTRY *EntPtr;
CHAR16 *CopyName;
// Check some initial invalid parameters for this function.
if ((Namespace == NULL) || (NewEntry == NULL)) {
return EFI_INVALID_PARAMETER;
}
if ((LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK) &&
(LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW) &&
(LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE))
{
return EFI_INVALID_PARAMETER;
}
//
// Set NameSize to suppress incorrect compiler/analyzer warnings
//
NameSize = 0;
// Now we've gotta determine the total size of the buffer required for
// the VariablePolicy structure.
TotalSize = sizeof (VARIABLE_POLICY_ENTRY);
if (Name != NULL) {
NameSize = StrnSizeS (Name, MAX_UINT16);
TotalSize += NameSize;
}
// Make sure the size fits within a VARIABLE_POLICY_ENTRY.Size.
ASSERT (TotalSize <= MAX_UINT16);
if (TotalSize > MAX_UINT16) {
return EFI_BUFFER_TOO_SMALL;
}
// Allocate a buffer to hold all the data. We're on the home stretch.
*NewEntry = AllocatePool (TotalSize);
if (*NewEntry == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// If we're still here, we're basically done.
// Copy the data and GET... OUT....
EntPtr = *NewEntry;
PopulateCommonData (
EntPtr,
Namespace,
MinSize,
MaxSize,
AttributesMustHave,
AttributesCantHave,
LockPolicyType
);
EntPtr->Size = (UINT16)TotalSize; // This is safe because we've already checked.
EntPtr->OffsetToName = sizeof (VARIABLE_POLICY_ENTRY);
if (Name != NULL) {
CopyName = (CHAR16 *)((UINT8 *)EntPtr + EntPtr->OffsetToName);
CopyMem (CopyName, Name, NameSize);
}
return EFI_SUCCESS;
}
/**
This helper function will allocate and populate a new VariablePolicy
structure for a policy with a lock type of VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE.
NOTE: Caller will need to free structure once finished.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name.
Otherwise, will create a policy that targets an entire namespace.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] VarStateNamespace Pointer to the EFI_GUID for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace.
@param[in] VarStateValue Value for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value.
@param[in] VarStateName Pointer to the CHAR16 array for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name.
@param[out] NewEntry If successful, will be set to a pointer to the allocated buffer containing the
new policy.
@retval EFI_SUCCESS Operation completed successfully and structure is populated.
@retval EFI_INVALID_PARAMETER Namespace, VarStateNamespace, VarStateName is NULL.
@retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in UINT16 size.
@retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space for structure.
**/
EFI_STATUS
EFIAPI
CreateVarStateVariablePolicy (
IN CONST EFI_GUID *Namespace,
IN CONST CHAR16 *Name OPTIONAL,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN CONST EFI_GUID *VarStateNamespace,
IN UINT8 VarStateValue,
IN CONST CHAR16 *VarStateName,
OUT VARIABLE_POLICY_ENTRY **NewEntry
)
{
UINTN TotalSize;
UINTN NameSize;
UINTN VarStateNameSize;
VARIABLE_POLICY_ENTRY *EntPtr;
CHAR16 *CopyName;
VARIABLE_LOCK_ON_VAR_STATE_POLICY *CopyPolicy;
// Check some initial invalid parameters for this function.
if ((Namespace == NULL) || (VarStateNamespace == NULL) ||
(VarStateName == NULL) || (NewEntry == NULL))
{
return EFI_INVALID_PARAMETER;
}
// Now we've gotta determine the total size of the buffer required for
// the VariablePolicy structure.
VarStateNameSize = StrnSizeS (VarStateName, MAX_UINT16);
TotalSize = sizeof (VARIABLE_POLICY_ENTRY) +
sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY) +
VarStateNameSize;
if (Name != NULL) {
NameSize = StrnSizeS (Name, MAX_UINT16);
TotalSize += NameSize;
}
// Make sure the size fits within a VARIABLE_POLICY_ENTRY.Size.
ASSERT (TotalSize <= MAX_UINT16);
if (TotalSize > MAX_UINT16) {
return EFI_BUFFER_TOO_SMALL;
}
// Allocate a buffer to hold all the data. We're on the home stretch.
*NewEntry = AllocatePool (TotalSize);
if (*NewEntry == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// If we're still here, we're basically done.
// Copy the data and GET... OUT....
EntPtr = *NewEntry;
PopulateCommonData (
EntPtr,
Namespace,
MinSize,
MaxSize,
AttributesMustHave,
AttributesCantHave,
VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE
);
EntPtr->Size = (UINT16)TotalSize; // This is safe because we've already checked.
EntPtr->OffsetToName = sizeof (VARIABLE_POLICY_ENTRY) +
sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY) +
(UINT16)VarStateNameSize;
CopyPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY *)((UINT8 *)EntPtr + sizeof (VARIABLE_POLICY_ENTRY));
CopyName = (CHAR16 *)((UINT8 *)CopyPolicy + sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY));
CopyGuid (&CopyPolicy->Namespace, VarStateNamespace);
CopyPolicy->Value = VarStateValue;
CopyMem (CopyName, VarStateName, VarStateNameSize);
if (Name != NULL) {
CopyName = (CHAR16 *)((UINT8 *)EntPtr + EntPtr->OffsetToName);
CopyMem (CopyName, Name, NameSize);
}
return EFI_SUCCESS;
}
/**
This helper function does everything that CreateBasicVariablePolicy() does, but also
uses the passed in protocol to register the policy with the infrastructure.
Does not return a buffer, does not require the caller to free anything.
@param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy protocol.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name.
Otherwise, will create a policy that targets an entire namespace.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] LockPolicyType LockPolicyType for the VariablePolicy.
@retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL.
@retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or RegisterVariablePolicy().
**/
EFI_STATUS
EFIAPI
RegisterBasicVariablePolicy (
IN EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy,
IN CONST EFI_GUID *Namespace,
IN CONST CHAR16 *Name OPTIONAL,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN UINT8 LockPolicyType
)
{
VARIABLE_POLICY_ENTRY *NewEntry;
EFI_STATUS Status;
// Check the simple things.
if (VariablePolicy == NULL) {
return EFI_INVALID_PARAMETER;
}
// Create the new entry and make sure that everything worked.
NewEntry = NULL;
Status = CreateBasicVariablePolicy (
Namespace,
Name,
MinSize,
MaxSize,
AttributesMustHave,
AttributesCantHave,
LockPolicyType,
&NewEntry
);
// If that was successful, attempt to register the new policy.
if (!EFI_ERROR (Status)) {
Status = VariablePolicy->RegisterVariablePolicy (NewEntry);
}
// If we allocated the buffer, free the buffer.
if (NewEntry != NULL) {
FreePool (NewEntry);
}
return Status;
}
/**
This helper function does everything that CreateBasicVariablePolicy() does, but also
uses the passed in protocol to register the policy with the infrastructure.
Does not return a buffer, does not require the caller to free anything.
@param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy protocol.
@param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect.
@param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name.
Otherwise, will create a policy that targets an entire namespace.
@param[in] MinSize MinSize for the VariablePolicy.
@param[in] MaxSize MaxSize for the VariablePolicy.
@param[in] AttributesMustHave AttributesMustHave for the VariablePolicy.
@param[in] AttributesCantHave AttributesCantHave for the VariablePolicy.
@param[in] VarStateNamespace Pointer to the EFI_GUID for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace.
@param[in] VarStateName Pointer to the CHAR16 array for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name.
@param[in] VarStateValue Value for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value.
@retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL.
@retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or RegisterVariablePolicy().
**/
EFI_STATUS
EFIAPI
RegisterVarStateVariablePolicy (
IN EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy,
IN CONST EFI_GUID *Namespace,
IN CONST CHAR16 *Name OPTIONAL,
IN UINT32 MinSize,
IN UINT32 MaxSize,
IN UINT32 AttributesMustHave,
IN UINT32 AttributesCantHave,
IN CONST EFI_GUID *VarStateNamespace,
IN CONST CHAR16 *VarStateName,
IN UINT8 VarStateValue
)
{
VARIABLE_POLICY_ENTRY *NewEntry;
EFI_STATUS Status;
// Check the simple things.
if (VariablePolicy == NULL) {
return EFI_INVALID_PARAMETER;
}
// Create the new entry and make sure that everything worked.
NewEntry = NULL;
Status = CreateVarStateVariablePolicy (
Namespace,
Name,
MinSize,
MaxSize,
AttributesMustHave,
AttributesCantHave,
VarStateNamespace,
VarStateValue,
VarStateName,
&NewEntry
);
// If that was successful, attempt to register the new policy.
if (!EFI_ERROR (Status)) {
Status = VariablePolicy->RegisterVariablePolicy (NewEntry);
}
// If we allocated the buffer, free the buffer.
if (NewEntry != NULL) {
FreePool (NewEntry);
}
return Status;
}

View File

@ -0,0 +1,35 @@
## @file VariablePolicyHelperLib.inf
# This library contains helper functions for marshalling and registering
# new policies with the VariablePolicy infrastructure.
#
# This library is currently written against VariablePolicy revision 0x00010000.
#
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
INF_VERSION = 0x00010017
BASE_NAME = VariablePolicyHelperLib
# MODULE_UNI_FILE = VariablePolicyHelperLib.uni
FILE_GUID = B3C2206B-FDD1-4AED-8352-FC5EC34C5630
VERSION_STRING = 1.0
MODULE_TYPE = BASE
LIBRARY_CLASS = VariablePolicyHelperLib
[Sources]
VariablePolicyHelperLib.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
BaseLib
DebugLib
MemoryAllocationLib
BaseMemoryLib

View File

@ -0,0 +1,12 @@
// /** @file
// VariablePolicyHelperLib.uni
//
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Library containing helper functions for marshalling and registering new policies with the VariablePolicy infrastructure"
#string STR_MODULE_DESCRIPTION #language en-US "Library containing helper functions for marshalling and registering new policies with the VariablePolicy infrastructure"

View File

@ -0,0 +1,406 @@
---
title: UEFI Variable Policy Whitepaper
version: 1.0
copyright: Copyright (c) Microsoft Corporation.
---
# UEFI Variable Policy
## Summary
UEFI Variable Policy spec aims to describe the DXE protocol interface
which allows enforcing certain rules on certain UEFI variables. The
protocol allows communication with the Variable Policy Engine which
performs the policy enforcement.
The Variable Policy is comprised of a set of policy entries which
describe, per UEFI variable (identified by namespace GUID and variable
name) the following rules:
- Required variable attributes
- Prohibited variable attributes
- Minimum variable size
- Maximum variable size
- Locking:
- Locking "immediately"
- Locking on creation
- Locking based on a state of another variable
The spec assumes that the Variable Policy Engine runs in a trusted
enclave, potentially off the main CPU that runs UEFI. For that reason,
it is assumed that the Variable Policy Engine has no concept of UEFI
events, and that the communication from the DXE driver to the trusted
enclave is proprietary.
At power-on, the Variable Policy Engine is:
- Enabled -- present policy entries are evaluated on variable access
calls.
- Unlocked -- new policy entries can be registered.
Policy is expected to be clear on power-on. Policy is volatile and not
preserved across system reset.
## DXE Protocol
```h
typedef struct {
UINT64 Revision;
DISABLE_VARIABLE_POLICY DisableVariablePolicy;
IS_VARIABLE_POLICY_ENABLED IsVariablePolicyEnabled;
REGISTER_VARIABLE_POLICY RegisterVariablePolicy;
DUMP_VARIABLE_POLICY DumpVariablePolicy;
LOCK_VARIABLE_POLICY LockVariablePolicy;
} _VARIABLE_POLICY_PROTOCOL;
typedef _VARIABLE_POLICY_PROTOCOL VARIABLE_POLICY_PROTOCOL;
extern EFI_GUID gVariablePolicyProtocolGuid;
```
```text
## Include/Protocol/VariablePolicy.h
gVariablePolicyProtocolGuid = { 0x81D1675C, 0x86F6, 0x48DF, { 0xBD, 0x95, 0x9A, 0x6E, 0x4F, 0x09, 0x25, 0xC3 } }
```
### DisableVariablePolicy
Function prototype:
```c
EFI_STATUS
EFIAPI
DisableVariablePolicy (
VOID
);
```
`DisableVariablePolicy` call disables the Variable Policy Engine, so
that the present policy entries are no longer taken into account on
variable access calls. This call effectively turns off the variable
policy verification for this boot. This also disables UEFI
Authenticated Variable protections including Secure Boot.
`DisableVariablePolicy` can only be called once during boot. If called
more than once, it will return `EFI_ALREADY_STARTED`. Note, this process
is irreversible until the next system reset -- there is no
"EnablePolicy" protocol function.
_IMPORTANT NOTE:_ It is strongly recommended that VariablePolicy *NEVER*
be disabled in "normal, production boot conditions". It is expected to always
be enforced. The most likely reasons to disable are for Manufacturing and
Refurbishing scenarios. If in doubt, leave the `gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable`
PCD set to `FALSE` and VariablePolicy will always be enabled.
### IsVariablePolicyEnabled
Function prototype:
```c
EFI_STATUS
EFIAPI
IsVariablePolicyEnabled (
OUT BOOLEAN *State
);
```
`IsVariablePolicyEnabled` accepts a pointer to a Boolean in which it
will store `TRUE` if Variable Policy Engine is enabled, or `FALSE` if
Variable Policy Engine is disabled. The function returns `EFI_SUCCESS`.
### RegisterVariablePolicy
Function prototype:
```c
EFI_STATUS
EFIAPI
RegisterVariablePolicy (
IN CONST VARIABLE_POLICY_ENTRY *PolicyEntry
);
```
`RegisterVariablePolicy` call accepts a pointer to a policy entry
structure and returns the status of policy registration. If the
Variable Policy Engine is not locked and the policy structures are
valid, the function will return `EFI_SUCCESS`. If the Variable Policy
Engine is locked, `RegisterVariablePolicy` call will return
`EFI_WRITE_PROTECTED` and will not register the policy entry. Bulk
registration is not supported at this time due to the requirements
around error handling on each policy registration.
Upon successful registration of a policy entry, Variable Policy Engine
will then evaluate this entry on subsequent variable access calls (as
long as Variable Policy Engine hasn't been disabled).
### DumpVariablePolicy
Function prototype:
```c
EFI_STATUS
EFIAPI
DumpVariablePolicy (
OUT UINT8 *Policy,
IN OUT UINT32 *Size
);
```
`DumpVariablePolicy` call accepts a pointer to a buffer and a pointer to
the size of the buffer as parameters and returns the status of placing
the policy into the buffer. On first call to `DumpVariablePolicy` one
should pass `NULL` as the buffer and a pointer to 0 as the `Size` variable
and `DumpVariablePolicy` will return `EFI_BUFFER_TOO_SMALL` and will
populate the `Size` parameter with the size of the needed buffer to
store the policy. This way, the caller can allocate the buffer of
correct size and call `DumpVariablePolicy` again. The function will
populate the buffer with policy and return `EFI_SUCCESS`.
### LockVariablePolicy
Function prototype:
```c
EFI_STATUS
EFIAPI
LockVariablePolicy (
VOID
);
```
`LockVariablePolicy` locks the Variable Policy Engine, i.e. prevents any
new policy entries from getting registered in this boot
(`RegisterVariablePolicy` calls will fail with `EFI_WRITE_PROTECTED`
status code returned).
## Policy Structure
The structure below is meant for the DXE protocol calling interface,
when communicating to the Variable Policy Engine, thus the pragma pack
directive. How these policies are stored in memory is up to the
implementation.
```c
#pragma pack(1)
typedef struct {
UINT32 Version;
UINT16 Size;
UINT16 OffsetToName;
EFI_GUID Namespace;
UINT32 MinSize;
UINT32 MaxSize;
UINT32 AttributesMustHave;
UINT32 AttributesCantHave;
UINT8 LockPolicyType;
UINT8 Reserved[3];
// UINT8 LockPolicy[]; // Variable Length Field
// CHAR16 Name[]; // Variable Length Field
} VARIABLE_POLICY_ENTRY;
```
The struct `VARIABLE_POLICY_ENTRY` above describes the layout for a policy
entry. The first element, `Size`, is the size of the policy entry, then
followed by `OffsetToName` -- the number of bytes from the beginning of
the struct to the name of the UEFI variable targeted by the policy
entry. The name can contain wildcards to match more than one variable,
more on this in the Wildcards section. The rest of the struct elements
are self-explanatory.
```cpp
#define VARIABLE_POLICY_TYPE_NO_LOCK 0
#define VARIABLE_POLICY_TYPE_LOCK_NOW 1
#define VARIABLE_POLICY_TYPE_LOCK_ON_CREATE 2
#define VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE 3
```
`LockPolicyType` can have the following values:
- `VARIABLE_POLICY_TYPE_NO_LOCK` -- means that no variable locking is performed. However,
the attribute and size constraints are still enforced. LockPolicy
field is size 0.
- `VARIABLE_POLICY_TYPE_LOCK_NOW` -- means that the variable starts being locked
immediately after policy entry registration. If the variable doesn't
exist at this point, being LockedNow means it cannot be created on
this boot. LockPolicy field is size 0.
- `VARIABLE_POLICY_TYPE_LOCK_ON_CREATE` -- means that the variable starts being locked
after it is created. This allows for variable creation and
protection after LockVariablePolicy() function has been called. The
LockPolicy field is size 0.
- `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE` -- means that the Variable Policy Engine will
examine the state/contents of another variable to determine if the
variable referenced in the policy entry is locked.
```c
typedef struct {
EFI_GUID Namespace;
UINT8 Value;
UINT8 Reserved;
// CHAR16 Name[]; // Variable Length Field
} VARIABLE_LOCK_ON_VAR_STATE_POLICY;
```
If `LockPolicyType` is `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`, then the final element in the
policy entry struct is of type `VARIABLE_LOCK_ON_VAR_STATE_POLICY`, which
lists the namespace GUID, name (no wildcards here), and value of the
variable which state determines the locking of the variable referenced
in the policy entry. The "locking" variable must be 1 byte in terms of
payload size. If the Referenced variable contents match the Value of the
`VARIABLE_LOCK_ON_VAR_STATE_POLICY` structure, the lock will be considered
active and the target variable will be locked. If the Reference variable
does not exist (ie. returns `EFI_NOT_FOUND`), this policy will be
considered inactive.
## Variable Name Wildcards
Two types of wildcards can be used in the UEFI variable name field in a
policy entry:
1. If the Name is a zero-length array (easily checked by comparing
fields `Size` and `OffsetToName` -- if they're the same, then the
`Name` is zero-length), then all variables in the namespace specified
by the provided GUID are targeted by the policy entry.
2. Character "#" in the `Name` corresponds to one numeric character
(0-9, A-F, a-f). For example, string "Boot####" in the `Name`
field of the policy entry will make it so that the policy entry will
target variables named "Boot0001", "Boot0002", etc.
Given the above two types of wildcards, one variable can be targeted by
more than one policy entry, thus there is a need to establish the
precedence rule: a more specific match is applied. When a variable
access operation is performed, Variable Policy Engine should first check
the variable being accessed against the policy entries without
wildcards, then with 1 wildcard, then with 2 wildcards, etc., followed
in the end by policy entries that match the whole namespace. One can
still imagine a situation where two policy entries with the same number
of wildcards match the same variable -- for example, policy entries with
Names "Boot00##" and "Boot##01" will both match variable "Boot0001".
Such situation can (and should) be avoided by designing mutually
exclusive Name strings with wildcards, however, if it occurs, then the
policy entry that was registered first will be used. After the most
specific match is selected, all other policies are ignored.
## Available Testing
This functionality is current supported by two kinds of tests: there is a host-based
unit test for the core business logic (this test accompanies the `VariablePolicyLib`
implementation that lives in `MdeModulePkg/Library`) and there is a functional test
for the protocol and its interfaces (this test lives in the `MdeModulePkg/Test/ShellTest`
directory).
### Host-Based Unit Test
There is a test that can be run as part of the Host-Based Unit Testing
infrastructure provided by EDK2 PyTools (documented elsewhere). It will test
all internal guarantees and is where you will find test cases for most of the
policy matching and security of the Variable Policy Engine.
### Shell-Based Functional Test
This test -- [Variable Policy Functional Unit Test](https://github.com/microsoft/mu_plus/tree/release/202005/UefiTestingPkg/FunctionalSystemTests/VarPolicyUnitTestApp) -- can be built as a
UEFI Shell application and run to validate that the Variable Policy Engine
is correctly installed and enforcing policies on the target system.
NOTE: This test _must_ be run prior to calling `DisableVariablePolicy` for all
test cases to pass. For this reason, it is recommended to run this on a test-built
FW for complete results, and then again on a production-built FW for release
results.
## Use Cases
The below examples are hypothetical scenarios based on real-world requirements
that demonstrate how Variable Policies could be constructed to solve various
problems.
### UEFI Setup Variables (Example 1)
Variables containing values of the setup options exposed via UEFI
menu (setup variables). These would be locked based on a state of
another variable, "ReadyToBoot", which would be set to 1 at the
ReadyToBoot event. Thus, the policy for the setup variables would be
of type `LockOnVarState`, with the "ReadyToBoot" listed as the name of
the variable, appropriate GUID listed as the namespace, and 1 as
value. Entry into the trusted UEFI menu app doesn't signal
ReadyToBoot, but booting to any device does, and the setup variables
are write-protected. The "ReadyToBoot" variable would need to be
locked-on-create. *(THIS IS ESSENTIALLY LOCK ON EVENT, BUT SINCE THE
POLICY ENGINE IS NOT IN THE UEFI ENVIRONMENT VARIABLES ARE USED)*
For example, "AllowPXEBoot" variable locked by "ReadyToBoot" variable.
(NOTE: In the below example, the emphasized fields ('Namespace', 'Value', and 'Name')
are members of the `VARIABLE_LOCK_ON_VAR_STATE_POLICY` structure.)
Size | ...
---- | ---
OffsetToName | ...
NameSpace | ...
MinSize | ...
MaxSize | ...
AttributesMustHave | ...
AttributesCantHave | ...
LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`
_Namespace_ | ...
_Value_ | 1
_Name_ | "ReadyToBoot"
//Name | "AllowPXEBoot"
### Manufacturing VPD (Example 2)
Manufacturing Variable Provisioning Data (VPD) is stored in
variables and is created while in Manufacturing (MFG) Mode. In MFG
Mode Variable Policy Engine is disabled, thus these VPD variables
can be created. These variables are locked with lock policy type
`LockNow`, so that these variables can't be tampered with in Customer
Mode. To overwrite or clear VPD, the device would need to MFG mode,
which is standard practice for refurbishing/remanufacturing
scenarios.
Example: "DisplayPanelCalibration" variable...
Size | ...
---- | ---
OffsetToName | ...
NameSpace | ...
MinSize | ...
MaxSize | ...
AttributesMustHave | ...
AttributesCantHave | ...
LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_NOW`
// Name | "DisplayPanelCalibration"
### 3rd Party Calibration Data (Example 3)
Bluetooth pre-pairing variables are locked-on-create because these
get created by an OS application when Variable Policy is in effect.
Example: "KeyboardBTPairing" variable
Size | ...
---- | ---
OffsetToName | ...
NameSpace | ...
MinSize | ...
MaxSize | ...
AttributesMustHave | ...
AttributesCantHave | ...
LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_CREATE`
// Name | "KeyboardBTPairing"
### Software-based Variable Policy (Example 4)
Example: "Boot####" variables (a name string with wildcards that
will match variables "Boot0000" to "BootFFFF") locked by "LockBootOrder"
variable.
Size | ...
---- | ---
OffsetToName | ...
NameSpace | ...
MinSize | ...
MaxSize | ...
AttributesMustHave | ...
AttributesCantHave | ...
LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`
_Namespace_ | ...
_Value_ | 1
_Name_ | "LockBootOrder"
//Name | "Boot####"

View File

@ -0,0 +1,44 @@
/** @file -- VariablePolicyExtraInitNull.c
This file contains extra init and deinit routines that don't do anything
extra.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/UefiRuntimeServicesTableLib.h>
/**
An extra init hook that enables the RuntimeDxe library instance to
register VirtualAddress change callbacks. Among other things.
@retval EFI_SUCCESS Everything is good. Continue with init.
@retval Others Uh... don't continue.
**/
EFI_STATUS
VariablePolicyExtraInit (
VOID
)
{
// NULL implementation.
return EFI_SUCCESS;
}
/**
An extra deinit hook that enables the RuntimeDxe library instance to
register VirtualAddress change callbacks. Among other things.
@retval EFI_SUCCESS Everything is good. Continue with deinit.
@retval Others Uh... don't continue.
**/
EFI_STATUS
VariablePolicyExtraDeinit (
VOID
)
{
// NULL implementation.
return EFI_SUCCESS;
}

View File

@ -0,0 +1,84 @@
/** @file -- VariablePolicyExtraInitRuntimeDxe.c
This file contains extra init and deinit routines that register and unregister
VariableAddressChange callbacks.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
extern EFI_GET_VARIABLE mGetVariableHelper;
extern UINT8 *mPolicyTable;
STATIC BOOLEAN mIsVirtualAddrConverted;
STATIC EFI_EVENT mVariablePolicyLibVirtualAddressChangeEvent = NULL;
/**
For the RuntimeDxe version of this lib, convert internal pointer addresses to virtual addresses.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context The pointer to the notification function's context, which
is implementation-dependent.
**/
STATIC
VOID
EFIAPI
VariablePolicyLibVirtualAddressCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
gRT->ConvertPointer (0, (VOID **)&mPolicyTable);
gRT->ConvertPointer (0, (VOID **)&mGetVariableHelper);
mIsVirtualAddrConverted = TRUE;
}
/**
An extra init hook that enables the RuntimeDxe library instance to
register VirtualAddress change callbacks. Among other things.
@retval EFI_SUCCESS Everything is good. Continue with init.
@retval Others Uh... don't continue.
**/
EFI_STATUS
VariablePolicyExtraInit (
VOID
)
{
return gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
VariablePolicyLibVirtualAddressCallback,
NULL,
&gEfiEventVirtualAddressChangeGuid,
&mVariablePolicyLibVirtualAddressChangeEvent
);
}
/**
An extra deinit hook that enables the RuntimeDxe library instance to
register VirtualAddress change callbacks. Among other things.
@retval EFI_SUCCESS Everything is good. Continue with deinit.
@retval Others Uh... don't continue.
**/
EFI_STATUS
VariablePolicyExtraDeinit (
VOID
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
if (mIsVirtualAddrConverted) {
Status = gBS->CloseEvent (mVariablePolicyLibVirtualAddressChangeEvent);
} else {
Status = EFI_SUCCESS;
}
return Status;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
## @file VariablePolicyLib.inf
# Business logic for Variable Policy enforcement.
#
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
INF_VERSION = 0x00010017
BASE_NAME = VariablePolicyLib
FILE_GUID = E9ECD342-159A-4F24-9FDF-65724027C594
VERSION_STRING = 1.0
MODULE_TYPE = DXE_DRIVER
LIBRARY_CLASS = VariablePolicyLib|DXE_DRIVER DXE_SMM_DRIVER MM_STANDALONE
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = ANY
#
[Sources]
VariablePolicyLib.c
VariablePolicyExtraInitNull.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
BaseLib
DebugLib
BaseMemoryLib
MemoryAllocationLib
SafeIntLib
PcdLib
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
[BuildOptions]
MSFT:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
GCC:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST

View File

@ -0,0 +1,12 @@
// /** @file
// VariablePolicyLib.uni
//
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Library containing the business logic for the VariablePolicy engine"
#string STR_MODULE_DESCRIPTION #language en-US "Library containing the business logic for the VariablePolicy engine"

View File

@ -0,0 +1,51 @@
## @file VariablePolicyLibRuntimeDxe.inf
# Business logic for Variable Policy enforcement.
# This instance is specifically for RuntimeDxe and contains
# extra routines to register for VirtualAddressChangeEvents.
#
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
INF_VERSION = 0x00010017
BASE_NAME = VariablePolicyLibRuntimeDxe
FILE_GUID = 205F7F0E-8EAC-4914-8390-1B90DD7E2A27
VERSION_STRING = 1.0
MODULE_TYPE = DXE_RUNTIME_DRIVER
LIBRARY_CLASS = VariablePolicyLib|DXE_RUNTIME_DRIVER
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = ANY
#
[Sources]
VariablePolicyLib.c
VariablePolicyExtraInitRuntimeDxe.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
DebugLib
BaseMemoryLib
MemoryAllocationLib
SafeIntLib
UefiBootServicesTableLib
UefiRuntimeServicesTableLib
PcdLib
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
[Guids]
gEfiEventVirtualAddressChangeGuid

View File

@ -620,6 +620,12 @@
# 0x80000006 | Incorrect error code provided.
#
## Include/Protocol/VariablePolicy.h
gEdkiiVariablePolicyProtocolGuid = { 0x81D1675C, 0x86F6, 0x48DF, { 0xBD, 0x95, 0x9A, 0x6E, 0x4F, 0x09, 0x25, 0xC3 } }
## Include/Protocol/UsbEthernetProtocol.h
gEdkIIUsbEthProtocolGuid = { 0x8d8969cc, 0xfeb0, 0x4303, { 0xb2, 0x1a, 0x1f, 0x11, 0x6f, 0x38, 0x56, 0x43 } }
[PcdsFeatureFlag]
## Indicates if the platform can support update capsule across a system reset.<BR><BR>
# TRUE - Supports update capsule across a system reset.<BR>
@ -1116,6 +1122,15 @@
# @Prompt Variable storage size.
gEfiMdeModulePkgTokenSpaceGuid.PcdVariableStoreSize|0x10000|UINT32|0x30000005
## Toggle for whether the VariablePolicy engine should allow disabling.
# The engine is enabled at power-on, but the interface allows the platform to
# disable enforcement for servicing flexibility. If this PCD is disabled, it will block the ability to
# disable the enforcement and VariablePolicy enforcement will always be ON.
# TRUE - VariablePolicy can be disabled by request through the interface (until interface is locked)
# FALSE - VariablePolicy interface will not accept requests to disable and is ALWAYS ON
# @Prompt Allow VariablePolicy enforcement to be disabled.
gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable|FALSE|BOOLEAN|0x30000020
## FFS filename to find the ACPI tables.
# @Prompt FFS name of ACPI tables storage.
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile|{ 0x25, 0x4e, 0x37, 0x7e, 0x01, 0x8e, 0xee, 0x4f, 0x87, 0xf2, 0x39, 0xc, 0x23, 0xc6, 0x6, 0xcd }|VOID*|0x30000016

View File

@ -803,6 +803,47 @@ typedef UINTN *BASE_LIST;
#define OFFSET_OF(TYPE, Field) ((UINTN) &(((TYPE *)0)->Field))
#endif
/**
Returns the alignment requirement of a type.
@param TYPE The name of the type to retrieve the alignment requirement of.
@return Alignment requirement, in Bytes, of TYPE.
**/
#if defined(__cplusplus) && __cplusplus >= 201103L
//
// C++ 11 and later support the standard operator alignof.
//
#define ALIGNOF(TYPE) alignof (TYPE)
#elif ((defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1900)) && !defined (__cplusplus)) || defined(__clang__)
//
// All supported versions of GCC and Clang, as well as MSVC 2015 and later,
// support the standard operator _Alignof in C mode. GCC and MSVC do not
// support it in C++ mode though.
//
#define ALIGNOF(TYPE) _Alignof (TYPE)
#elif defined(__GNUC__)
//
// GCC does not support _Alignof in C++ mode, unlike Clang. The vendor-
// extenstion is supported in both C and C++ mode.
//
#define ALIGNOF(TYPE) __alignof__ (TYPE)
#elif defined(_MSC_EXTENSIONS)
//
// Earlier versions of MSVC, at least MSVC 2008 and later, as well as current
// versions in C++ mode support the vendor-extension __alignof.
//
#define ALIGNOF(TYPE) __alignof (TYPE)
#else
//
// For compilers that do not support inbuilt alignof operators, use OFFSET_OF.
// CHAR8 is known to have both a size and an alignment requirement of 1 Byte.
// As such, A must be located exactly at the offset equal to its alignment
// requirement.
//
#define ALIGNOF(TYPE) OFFSET_OF (struct { CHAR8 C; TYPE A; }, A)
#endif
/**
Portable definition for compile time assertions.
Equivalent to C11 static_assert macro from assert.h.
@ -887,6 +928,60 @@ STATIC_ASSERT (sizeof (__VERIFY_UINT32_ENUM_SIZE) == 4, "Size of enum does not m
**/
#define BASE_CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - OFFSET_OF (TYPE, Field)))
/**
Checks whether a value is a power of two.
@param Value The value to check.
@retval TRUE Value is a power of two.
@retval FALSE Value is not a power of two.
**/
#define IS_POW2(Value) ((Value) != 0U && ((Value) & ((Value) - 1U)) == 0U)
/**
Determines the subtrahend to subtract from a value to round it down to the
previous boundary of a specified alignment.
@param Value The value to round down.
@param Alignment The alignment boundary used to return the subtrahend.
@return Subtrahend to round Value down to alignment boundary Alignment.
**/
#define ALIGN_VALUE_SUBTRAHEND(Value, Alignment) ((Value) & ((Alignment) - 1U))
/**
Checks whether a value is aligned by a specified alignment.
@param Value The value to check.
@param Alignment The alignment boundary used to check against.
@retval TRUE Value is aligned by Alignment.
@retval FALSE Value is not aligned by Alignment.
**/
#define IS_ALIGNED(Value, Alignment) (ALIGN_VALUE_SUBTRAHEND (Value, Alignment) == 0U)
/**
Checks whether a pointer or address is aligned by a specified alignment.
@param Address The pointer or address to check.
@param Alignment The alignment boundary used to check against.
@retval TRUE Address is aligned by Alignment.
@retval FALSE Address is not aligned by Alignment.
**/
#define ADDRESS_IS_ALIGNED(Address, Alignment) IS_ALIGNED ((UINTN) (Address), Alignment)
/**
Determines the addend to add to a value to round it up to the next boundary of
a specified alignment.
@param Value The value to round up.
@param Alignment The alignment boundary used to return the addend.
@return Addend to round Value up to alignment boundary Alignment.
**/
#define ALIGN_VALUE_ADDEND(Value, Alignment) (((Alignment) - (Value)) & ((Alignment) - 1U))
/**
Rounds a value up to the next boundary using a specified alignment.

View File

@ -56,12 +56,11 @@ typedef struct {
/// WIN_CERTIFICATE_UEFI_GUID.CertData
///
typedef struct {
EFI_GUID HashType;
GUID HashType;
UINT8 PublicKey[256];
UINT8 Signature[256];
} EFI_CERT_BLOCK_RSA_2048_SHA256;
///
/// Certificate which encapsulates a GUID-specific digital signature
///
@ -75,7 +74,7 @@ typedef struct {
/// This is the unique id which determines the
/// format of the CertData. .
///
EFI_GUID CertType;
GUID CertType;
///
/// The following is the certificate data. The format of
/// the data is determined by the CertType.
@ -85,7 +84,6 @@ typedef struct {
UINT8 CertData[1];
} WIN_CERTIFICATE_UEFI_GUID;
///
/// Certificate which encapsulates the RSASSA_PKCS1-v1_5 digital signature.
///
@ -104,7 +102,7 @@ typedef struct {
/// This is the hashing algorithm which was performed on the
/// UEFI executable when creating the digital signature.
///
EFI_GUID HashAlgorithm;
GUID HashAlgorithm;
///
/// The following is the actual digital signature. The
/// size of the signature is the same size as the key
@ -117,6 +115,6 @@ typedef struct {
///
} WIN_CERTIFICATE_EFI_PKCS1_15;
extern EFI_GUID gEfiCertTypeRsa2048Sha256Guid;
extern GUID gEfiCertTypeRsa2048Sha256Guid;
#endif

View File

@ -51,7 +51,6 @@
///
#define HTTP_HEADER_ACCEPT "Accept"
///
/// Accept-Charset Request Header
/// The Accept-Charset request-header field can be used to indicate what character sets
@ -76,7 +75,6 @@
///
#define HTTP_HEADER_ACCEPT_RANGES "Accept-Ranges"
///
/// Accept-Encoding Request Header
/// The Accept-Encoding request-header field is similar to Accept,
@ -105,7 +103,6 @@
#define HTTP_CONTENT_ENCODING_DEFLATE "deflate" /// The "zlib" format defined in RFC 1950 in combination with the "deflate"
/// compression mechanism described in RFC 1951.
///
/// Content-Type Header
/// The Content-Type entity-header field indicates the media type of the entity-body sent to
@ -129,7 +126,6 @@
#define HTTP_CONTENT_TYPE_IMAGE_PNG "image/png"
#define HTTP_CONTENT_TYPE_IMAGE_SVG_XML "image/svg+xml"
///
/// Content-Length Header
/// The Content-Length entity-header field indicates the size of the entity-body,
@ -146,7 +142,11 @@
/// is a property of the message, not of the entity.
///
#define HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding"
#define HTTP_HEADER_TRANSFER_ENCODING_CHUNKED "chunked"
#define CHUNKED_TRANSFER_CODING_CR '\r'
#define CHUNKED_TRANSFER_CODING_LF '\n'
#define CHUNKED_TRANSFER_CODING_LAST_CHUNK '0'
#define CHUNKED_TRANSFER_CODING_EXTENSION_SEPARATOR ';'
///
/// User Agent Request Header
@ -193,7 +193,6 @@
///
#define HTTP_HEADER_IF_MATCH "If-Match"
///
/// The If-None-Match request-header field is used with a method to make it conditional.
/// A client that has one or more entities previously obtained from the resource can verify
@ -205,7 +204,13 @@
///
#define HTTP_HEADER_IF_NONE_MATCH "If-None-Match"
///
/// The WWW-Authenticate Response Header
/// If a server receives a request for an access-protected object, and an
/// acceptable Authorization header is not sent, the server responds with
/// a "401 Unauthorized" status code, and a WWW-Authenticate header.
///
#define HTTP_HEADER_WWW_AUTHENTICATE "WWW-Authenticate"
///
/// Authorization Request Header

View File

@ -0,0 +1,737 @@
/** @file
EFI image format for PE32, PE32+ and TE. Please note some data structures are
different for PE32 and PE32+. EFI_IMAGE_NT_HEADERS32 is for PE32 and
EFI_IMAGE_NT_HEADERS64 is for PE32+.
This file is coded to the Visual Studio, Microsoft Portable Executable and
Common Object File Format Specification, Revision 8.3 - February 6, 2013.
This file also includes some definitions in PI Specification, Revision 1.0.
Copyright (c) 2020, Marvin Häuser. All rights reserved.<BR>
Copyright (c) 2020, Vitaly Cheptsov. All rights reserved.<BR>
Copyright (c) 2020, ISP RAS. All rights reserved.<BR>
Portions copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
Portions copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
Portions Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#ifndef PE_COFF_IMAGE2_H_
#define PE_COFF_IMAGE2_H_
//
// PE32+ Subsystem type for EFI images
//
#define EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION 10
#define EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
#define EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
#define EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER 13 ///< defined PI Specification, 1.0
//
// PE32+ Machine type for EFI images
//
#define IMAGE_FILE_MACHINE_I386 0x014c
#define IMAGE_FILE_MACHINE_IA64 0x0200
#define IMAGE_FILE_MACHINE_EBC 0x0EBC
#define IMAGE_FILE_MACHINE_X64 0x8664
#define IMAGE_FILE_MACHINE_ARMTHUMB_MIXED 0x01c2
#define IMAGE_FILE_MACHINE_ARM64 0xAA64
#define IMAGE_FILE_MACHINE_RISCV32 0x5032
#define IMAGE_FILE_MACHINE_RISCV64 0x5064
#define IMAGE_FILE_MACHINE_RISCV128 0x5128
#define IMAGE_FILE_MACHINE_LOONGARCH32 0x6232
#define IMAGE_FILE_MACHINE_LOONGARCH64 0x6264
//
// EXE file formats
//
#define EFI_IMAGE_DOS_SIGNATURE SIGNATURE_16('M', 'Z')
#define EFI_IMAGE_OS2_SIGNATURE SIGNATURE_16('N', 'E')
#define EFI_IMAGE_OS2_SIGNATURE_LE SIGNATURE_16('L', 'E')
#define EFI_IMAGE_NT_SIGNATURE SIGNATURE_32('P', 'E', '\0', '\0')
///
/// PE images can start with an optional DOS header, so if an image is run
/// under DOS it can print an error message.
///
typedef struct {
UINT16 e_magic; ///< Magic number.
UINT16 e_cblp; ///< Bytes on last page of file.
UINT16 e_cp; ///< Pages in file.
UINT16 e_crlc; ///< Relocations.
UINT16 e_cparhdr; ///< Size of header in paragraphs.
UINT16 e_minalloc; ///< Minimum extra paragraphs needed.
UINT16 e_maxalloc; ///< Maximum extra paragraphs needed.
UINT16 e_ss; ///< Initial (relative) SS value.
UINT16 e_sp; ///< Initial SP value.
UINT16 e_csum; ///< Checksum.
UINT16 e_ip; ///< Initial IP value.
UINT16 e_cs; ///< Initial (relative) CS value.
UINT16 e_lfarlc; ///< File address of relocation table.
UINT16 e_ovno; ///< Overlay number.
UINT16 e_res[4]; ///< Reserved words.
UINT16 e_oemid; ///< OEM identifier (for e_oeminfo).
UINT16 e_oeminfo; ///< OEM information; e_oemid specific.
UINT16 e_res2[10]; ///< Reserved words.
UINT32 e_lfanew; ///< File address of new exe header.
} EFI_IMAGE_DOS_HEADER;
///
/// COFF File Header (Object and Image).
///
typedef struct {
UINT16 Machine;
UINT16 NumberOfSections;
UINT32 TimeDateStamp;
UINT32 PointerToSymbolTable;
UINT32 NumberOfSymbols;
UINT16 SizeOfOptionalHeader;
UINT16 Characteristics;
} EFI_IMAGE_FILE_HEADER;
///
/// Size of EFI_IMAGE_FILE_HEADER.
///
#define EFI_IMAGE_SIZEOF_FILE_HEADER 20
//
// Characteristics
//
#define EFI_IMAGE_FILE_RELOCS_STRIPPED BIT0 ///< 0x0001 Relocation info stripped from file.
#define EFI_IMAGE_FILE_EXECUTABLE_IMAGE BIT1 ///< 0x0002 File is executable (i.e. no unresolved externel references).
#define EFI_IMAGE_FILE_LINE_NUMS_STRIPPED BIT2 ///< 0x0004 Line numbers stripped from file.
#define EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED BIT3 ///< 0x0008 Local symbols stripped from file.
#define EFI_IMAGE_FILE_LARGE_ADDRESS_AWARE BIT5 ///< 0x0020 Supports addresses > 2-GB
#define EFI_IMAGE_FILE_BYTES_REVERSED_LO BIT7 ///< 0x0080 Bytes of machine word are reversed.
#define EFI_IMAGE_FILE_32BIT_MACHINE BIT8 ///< 0x0100 32 bit word machine.
#define EFI_IMAGE_FILE_DEBUG_STRIPPED BIT9 ///< 0x0200 Debugging info stripped from file in .DBG file.
#define EFI_IMAGE_FILE_SYSTEM BIT12 ///< 0x1000 System File.
#define EFI_IMAGE_FILE_DLL BIT13 ///< 0x2000 File is a DLL.
#define EFI_IMAGE_FILE_BYTES_REVERSED_HI BIT15 ///< 0x8000 Bytes of machine word are reversed.
///
/// Header Data Directories.
///
typedef struct {
UINT32 VirtualAddress;
UINT32 Size;
} EFI_IMAGE_DATA_DIRECTORY;
//
// Directory Entries
//
#define EFI_IMAGE_DIRECTORY_ENTRY_EXPORT 0
#define EFI_IMAGE_DIRECTORY_ENTRY_IMPORT 1
#define EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE 2
#define EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
#define EFI_IMAGE_DIRECTORY_ENTRY_SECURITY 4
#define EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC 5
#define EFI_IMAGE_DIRECTORY_ENTRY_DEBUG 6
#define EFI_IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
#define EFI_IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
#define EFI_IMAGE_DIRECTORY_ENTRY_TLS 9
#define EFI_IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
#define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16
///
/// @attention
/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and
/// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary
/// after NT additional fields.
///
#define EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
///
/// @attention
/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and
/// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary
/// after NT additional fields.
///
#define EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
///
/// @attention
/// EFI_IMAGE_NT_HEADERS32 is for use ONLY by tools.
///
typedef struct {
UINT32 Signature;
EFI_IMAGE_FILE_HEADER FileHeader;
} EFI_IMAGE_NT_HEADERS_COMMON_HDR;
STATIC_ASSERT (
sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR) == sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER),
"Unsupported padding."
);
///
/// @attention
/// EFI_IMAGE_NT_HEADERS32 is for use ONLY by tools.
///
typedef struct {
EFI_IMAGE_NT_HEADERS_COMMON_HDR CommonHeader;
///
/// Standard fields.
///
UINT16 Magic;
UINT8 MajorLinkerVersion;
UINT8 MinorLinkerVersion;
UINT32 SizeOfCode;
UINT32 SizeOfInitializedData;
UINT32 SizeOfUninitializedData;
UINT32 AddressOfEntryPoint;
UINT32 BaseOfCode;
UINT32 BaseOfData; ///< PE32 contains this additional field, which is absent in PE32+.
///
/// Optional Header Windows-Specific Fields.
///
UINT32 ImageBase;
UINT32 SectionAlignment;
UINT32 FileAlignment;
UINT16 MajorOperatingSystemVersion;
UINT16 MinorOperatingSystemVersion;
UINT16 MajorImageVersion;
UINT16 MinorImageVersion;
UINT16 MajorSubsystemVersion;
UINT16 MinorSubsystemVersion;
UINT32 Win32VersionValue;
UINT32 SizeOfImage;
UINT32 SizeOfHeaders;
UINT32 CheckSum;
UINT16 Subsystem;
UINT16 DllCharacteristics;
UINT32 SizeOfStackReserve;
UINT32 SizeOfStackCommit;
UINT32 SizeOfHeapReserve;
UINT32 SizeOfHeapCommit;
UINT32 LoaderFlags;
UINT32 NumberOfRvaAndSizes;
EFI_IMAGE_DATA_DIRECTORY DataDirectory[];
} EFI_IMAGE_NT_HEADERS32;
///
/// @attention
/// EFI_IMAGE_HEADERS64 is for use ONLY by tools.
///
typedef struct {
EFI_IMAGE_NT_HEADERS_COMMON_HDR CommonHeader;
///
/// Standard fields.
///
UINT16 Magic;
UINT8 MajorLinkerVersion;
UINT8 MinorLinkerVersion;
UINT32 SizeOfCode;
UINT32 SizeOfInitializedData;
UINT32 SizeOfUninitializedData;
UINT32 AddressOfEntryPoint;
UINT32 BaseOfCode;
///
/// Optional Header Windows-Specific Fields.
///
UINT64 ImageBase;
UINT32 SectionAlignment;
UINT32 FileAlignment;
UINT16 MajorOperatingSystemVersion;
UINT16 MinorOperatingSystemVersion;
UINT16 MajorImageVersion;
UINT16 MinorImageVersion;
UINT16 MajorSubsystemVersion;
UINT16 MinorSubsystemVersion;
UINT32 Win32VersionValue;
UINT32 SizeOfImage;
UINT32 SizeOfHeaders;
UINT32 CheckSum;
UINT16 Subsystem;
UINT16 DllCharacteristics;
UINT64 SizeOfStackReserve;
UINT64 SizeOfStackCommit;
UINT64 SizeOfHeapReserve;
UINT64 SizeOfHeapCommit;
UINT32 LoaderFlags;
UINT32 NumberOfRvaAndSizes;
EFI_IMAGE_DATA_DIRECTORY DataDirectory[];
} EFI_IMAGE_NT_HEADERS64;
//
// Other Windows Subsystem Values
//
#define EFI_IMAGE_SUBSYSTEM_UNKNOWN 0
#define EFI_IMAGE_SUBSYSTEM_NATIVE 1
#define EFI_IMAGE_SUBSYSTEM_WINDOWS_GUI 2
#define EFI_IMAGE_SUBSYSTEM_WINDOWS_CUI 3
#define EFI_IMAGE_SUBSYSTEM_OS2_CUI 5
#define EFI_IMAGE_SUBSYSTEM_POSIX_CUI 7
///
/// Length of ShortName.
///
#define EFI_IMAGE_SIZEOF_SHORT_NAME 8
///
/// Section Table. This table immediately follows the optional header.
///
typedef struct {
UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME];
UINT32 VirtualSize;
UINT32 VirtualAddress;
UINT32 SizeOfRawData;
UINT32 PointerToRawData;
UINT32 PointerToRelocations;
UINT32 PointerToLinenumbers;
UINT16 NumberOfRelocations;
UINT16 NumberOfLinenumbers;
UINT32 Characteristics;
} EFI_IMAGE_SECTION_HEADER;
//
// Section Flags Values
//
#define EFI_IMAGE_SCN_TYPE_NO_PAD BIT3 ///< 0x00000008 ///< Reserved.
#define EFI_IMAGE_SCN_CNT_CODE BIT5 ///< 0x00000020
#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA BIT6 ///< 0x00000040
#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA BIT7 ///< 0x00000080
#define EFI_IMAGE_SCN_LNK_OTHER BIT8 ///< 0x00000100 ///< Reserved.
#define EFI_IMAGE_SCN_LNK_INFO BIT9 ///< 0x00000200 ///< Section contains comments or some other type of information.
#define EFI_IMAGE_SCN_LNK_REMOVE BIT11 ///< 0x00000800 ///< Section contents will not become part of image.
#define EFI_IMAGE_SCN_LNK_COMDAT BIT12 ///< 0x00001000
#define EFI_IMAGE_SCN_ALIGN_1BYTES BIT20 ///< 0x00100000
#define EFI_IMAGE_SCN_ALIGN_2BYTES BIT21 ///< 0x00200000
#define EFI_IMAGE_SCN_ALIGN_4BYTES (BIT20|BIT21) ///< 0x00300000
#define EFI_IMAGE_SCN_ALIGN_8BYTES BIT22 ///< 0x00400000
#define EFI_IMAGE_SCN_ALIGN_16BYTES (BIT20|BIT22) ///< 0x00500000
#define EFI_IMAGE_SCN_ALIGN_32BYTES (BIT21|BIT22) ///< 0x00600000
#define EFI_IMAGE_SCN_ALIGN_64BYTES (BIT20|BIT21|BIT22) ///< 0x00700000
#define EFI_IMAGE_SCN_MEM_DISCARDABLE BIT25 ///< 0x02000000
#define EFI_IMAGE_SCN_MEM_NOT_CACHED BIT26 ///< 0x04000000
#define EFI_IMAGE_SCN_MEM_NOT_PAGED BIT27 ///< 0x08000000
#define EFI_IMAGE_SCN_MEM_SHARED BIT28 ///< 0x10000000
#define EFI_IMAGE_SCN_MEM_EXECUTE BIT29 ///< 0x20000000
#define EFI_IMAGE_SCN_MEM_READ BIT30 ///< 0x40000000
#define EFI_IMAGE_SCN_MEM_WRITE BIT31 ///< 0x80000000
///
/// Size of a Symbol Table Record.
///
#define EFI_IMAGE_SIZEOF_SYMBOL 18
//
// Symbols have a section number of the section in which they are
// defined. Otherwise, section numbers have the following meanings:
//
#define EFI_IMAGE_SYM_UNDEFINED (UINT16) 0 ///< Symbol is undefined or is common.
#define EFI_IMAGE_SYM_ABSOLUTE (UINT16) -1 ///< Symbol is an absolute value.
#define EFI_IMAGE_SYM_DEBUG (UINT16) -2 ///< Symbol is a special debug item.
//
// Symbol Type (fundamental) values.
//
#define EFI_IMAGE_SYM_TYPE_NULL 0 ///< no type.
#define EFI_IMAGE_SYM_TYPE_VOID 1 ///< no valid type.
#define EFI_IMAGE_SYM_TYPE_CHAR 2 ///< type character.
#define EFI_IMAGE_SYM_TYPE_SHORT 3 ///< type short integer.
#define EFI_IMAGE_SYM_TYPE_INT 4
#define EFI_IMAGE_SYM_TYPE_LONG 5
#define EFI_IMAGE_SYM_TYPE_FLOAT 6
#define EFI_IMAGE_SYM_TYPE_DOUBLE 7
#define EFI_IMAGE_SYM_TYPE_STRUCT 8
#define EFI_IMAGE_SYM_TYPE_UNION 9
#define EFI_IMAGE_SYM_TYPE_ENUM 10 ///< enumeration.
#define EFI_IMAGE_SYM_TYPE_MOE 11 ///< member of enumeration.
#define EFI_IMAGE_SYM_TYPE_BYTE 12
#define EFI_IMAGE_SYM_TYPE_WORD 13
#define EFI_IMAGE_SYM_TYPE_UINT 14
#define EFI_IMAGE_SYM_TYPE_DWORD 15
//
// Symbol Type (derived) values.
//
#define EFI_IMAGE_SYM_DTYPE_NULL 0 ///< no derived type.
#define EFI_IMAGE_SYM_DTYPE_POINTER 1
#define EFI_IMAGE_SYM_DTYPE_FUNCTION 2
#define EFI_IMAGE_SYM_DTYPE_ARRAY 3
//
// Storage classes.
//
#define EFI_IMAGE_SYM_CLASS_END_OF_FUNCTION ((UINT8) -1)
#define EFI_IMAGE_SYM_CLASS_NULL 0
#define EFI_IMAGE_SYM_CLASS_AUTOMATIC 1
#define EFI_IMAGE_SYM_CLASS_EXTERNAL 2
#define EFI_IMAGE_SYM_CLASS_STATIC 3
#define EFI_IMAGE_SYM_CLASS_REGISTER 4
#define EFI_IMAGE_SYM_CLASS_EXTERNAL_DEF 5
#define EFI_IMAGE_SYM_CLASS_LABEL 6
#define EFI_IMAGE_SYM_CLASS_UNDEFINED_LABEL 7
#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8
#define EFI_IMAGE_SYM_CLASS_ARGUMENT 9
#define EFI_IMAGE_SYM_CLASS_STRUCT_TAG 10
#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_UNION 11
#define EFI_IMAGE_SYM_CLASS_UNION_TAG 12
#define EFI_IMAGE_SYM_CLASS_TYPE_DEFINITION 13
#define EFI_IMAGE_SYM_CLASS_UNDEFINED_STATIC 14
#define EFI_IMAGE_SYM_CLASS_ENUM_TAG 15
#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16
#define EFI_IMAGE_SYM_CLASS_REGISTER_PARAM 17
#define EFI_IMAGE_SYM_CLASS_BIT_FIELD 18
#define EFI_IMAGE_SYM_CLASS_BLOCK 100
#define EFI_IMAGE_SYM_CLASS_FUNCTION 101
#define EFI_IMAGE_SYM_CLASS_END_OF_STRUCT 102
#define EFI_IMAGE_SYM_CLASS_FILE 103
#define EFI_IMAGE_SYM_CLASS_SECTION 104
#define EFI_IMAGE_SYM_CLASS_WEAK_EXTERNAL 105
//
// type packing constants
//
#define EFI_IMAGE_N_BTMASK 017
#define EFI_IMAGE_N_TMASK 060
#define EFI_IMAGE_N_TMASK1 0300
#define EFI_IMAGE_N_TMASK2 0360
#define EFI_IMAGE_N_BTSHFT 4
#define EFI_IMAGE_N_TSHIFT 2
//
// Communal selection types.
//
#define EFI_IMAGE_COMDAT_SELECT_NODUPLICATES 1
#define EFI_IMAGE_COMDAT_SELECT_ANY 2
#define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE 3
#define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH 4
#define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE 5
//
// the following values only be referred in PeCoff, not defined in PECOFF.
//
#define EFI_IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1
#define EFI_IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2
#define EFI_IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3
///
/// Relocation format.
///
typedef struct {
UINT32 VirtualAddress;
UINT32 SymbolTableIndex;
UINT16 Type;
} EFI_IMAGE_RELOCATION;
///
/// Size of EFI_IMAGE_RELOCATION
///
#define EFI_IMAGE_SIZEOF_RELOCATION 10
//
// I386 relocation types.
//
#define EFI_IMAGE_REL_I386_ABSOLUTE 0x0000 ///< Reference is absolute, no relocation is necessary.
#define EFI_IMAGE_REL_I386_DIR16 0x0001 ///< Direct 16-bit reference to the symbols virtual address.
#define EFI_IMAGE_REL_I386_REL16 0x0002 ///< PC-relative 16-bit reference to the symbols virtual address.
#define EFI_IMAGE_REL_I386_DIR32 0x0006 ///< Direct 32-bit reference to the symbols virtual address.
#define EFI_IMAGE_REL_I386_DIR32NB 0x0007 ///< Direct 32-bit reference to the symbols virtual address, base not included.
#define EFI_IMAGE_REL_I386_SEG12 0x0009 ///< Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address.
#define EFI_IMAGE_REL_I386_SECTION 0x000A
#define EFI_IMAGE_REL_I386_SECREL 0x000B
#define EFI_IMAGE_REL_I386_REL32 0x0014 ///< PC-relative 32-bit reference to the symbols virtual address.
//
// x64 processor relocation types.
//
#define IMAGE_REL_AMD64_ABSOLUTE 0x0000
#define IMAGE_REL_AMD64_ADDR64 0x0001
#define IMAGE_REL_AMD64_ADDR32 0x0002
#define IMAGE_REL_AMD64_ADDR32NB 0x0003
#define IMAGE_REL_AMD64_REL32 0x0004
#define IMAGE_REL_AMD64_REL32_1 0x0005
#define IMAGE_REL_AMD64_REL32_2 0x0006
#define IMAGE_REL_AMD64_REL32_3 0x0007
#define IMAGE_REL_AMD64_REL32_4 0x0008
#define IMAGE_REL_AMD64_REL32_5 0x0009
#define IMAGE_REL_AMD64_SECTION 0x000A
#define IMAGE_REL_AMD64_SECREL 0x000B
#define IMAGE_REL_AMD64_SECREL7 0x000C
#define IMAGE_REL_AMD64_TOKEN 0x000D
#define IMAGE_REL_AMD64_SREL32 0x000E
#define IMAGE_REL_AMD64_PAIR 0x000F
#define IMAGE_REL_AMD64_SSPAN32 0x0010
/**
Returns the type of a Base Relocation.
@param[in] Relocation The composite Base Relocation value.
**/
#define IMAGE_RELOC_TYPE(Relocation) ((Relocation) >> 12U)
/**
Returns the target offset of a Base Relocation.
@param[in] Relocation The composite Base Relocation value.
**/
#define IMAGE_RELOC_OFFSET(Relocation) ((Relocation) & 0x0FFFU)
///
/// Based relocation format.
///
typedef struct {
UINT32 VirtualAddress;
UINT32 SizeOfBlock;
UINT16 Relocations[];
} EFI_IMAGE_BASE_RELOCATION_BLOCK;
//
// Based relocation types.
//
#define EFI_IMAGE_REL_BASED_ABSOLUTE 0
#define EFI_IMAGE_REL_BASED_HIGH 1
#define EFI_IMAGE_REL_BASED_LOW 2
#define EFI_IMAGE_REL_BASED_HIGHLOW 3
#define EFI_IMAGE_REL_BASED_HIGHADJ 4
#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR 5
#define EFI_IMAGE_REL_BASED_ARM_MOV32A 5
#define EFI_IMAGE_REL_BASED_ARM_MOV32T 7
#define EFI_IMAGE_REL_BASED_IA64_IMM64 9
#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR16 9
#define EFI_IMAGE_REL_BASED_DIR64 10
///
/// Relocation types of RISC-V processor.
///
#define EFI_IMAGE_REL_BASED_RISCV_HI20 5
#define EFI_IMAGE_REL_BASED_RISCV_LOW12I 7
#define EFI_IMAGE_REL_BASED_RISCV_LOW12S 8
///
/// Line number format.
///
typedef struct {
union {
UINT32 SymbolTableIndex; ///< Symbol table index of function name if Linenumber is 0.
UINT32 VirtualAddress; ///< Virtual address of line number.
} Type;
UINT16 Linenumber; ///< Line number.
} EFI_IMAGE_LINENUMBER;
///
/// Size of EFI_IMAGE_LINENUMBER.
///
#define EFI_IMAGE_SIZEOF_LINENUMBER 6
//
// Archive format.
//
#define EFI_IMAGE_ARCHIVE_START_SIZE 8
#define EFI_IMAGE_ARCHIVE_START "!<arch>\n"
#define EFI_IMAGE_ARCHIVE_END "`\n"
#define EFI_IMAGE_ARCHIVE_PAD "\n"
#define EFI_IMAGE_ARCHIVE_LINKER_MEMBER "/ "
#define EFI_IMAGE_ARCHIVE_LONGNAMES_MEMBER "// "
///
/// Archive Member Headers
///
typedef struct {
UINT8 Name[16]; ///< File member name - `/' terminated.
UINT8 Date[12]; ///< File member date - decimal.
UINT8 UserID[6]; ///< File member user id - decimal.
UINT8 GroupID[6]; ///< File member group id - decimal.
UINT8 Mode[8]; ///< File member mode - octal.
UINT8 Size[10]; ///< File member size - decimal.
UINT8 EndHeader[2]; ///< String to end header. (0x60 0x0A).
} EFI_IMAGE_ARCHIVE_MEMBER_HEADER;
///
/// Size of EFI_IMAGE_ARCHIVE_MEMBER_HEADER.
///
#define EFI_IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60
//
// DLL Support
//
///
/// Export Directory Table.
///
typedef struct {
UINT32 Characteristics;
UINT32 TimeDateStamp;
UINT16 MajorVersion;
UINT16 MinorVersion;
UINT32 Name;
UINT32 Base;
UINT32 NumberOfFunctions;
UINT32 NumberOfNames;
UINT32 AddressOfFunctions;
UINT32 AddressOfNames;
UINT32 AddressOfNameOrdinals;
} EFI_IMAGE_EXPORT_DIRECTORY;
///
/// Hint/Name Table.
///
typedef struct {
UINT16 Hint;
UINT8 Name[1];
} EFI_IMAGE_IMPORT_BY_NAME;
///
/// Import Address Table RVA (Thunk Table).
///
typedef struct {
union {
UINT32 Function;
UINT32 Ordinal;
EFI_IMAGE_IMPORT_BY_NAME *AddressOfData;
} u1;
} EFI_IMAGE_THUNK_DATA;
#define EFI_IMAGE_ORDINAL_FLAG BIT31 ///< Flag for PE32.
#define EFI_IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & EFI_IMAGE_ORDINAL_FLAG) != 0)
#define EFI_IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff)
///
/// Import Directory Table
///
typedef struct {
UINT32 Characteristics;
UINT32 TimeDateStamp;
UINT32 ForwarderChain;
UINT32 Name;
EFI_IMAGE_THUNK_DATA *FirstThunk;
} EFI_IMAGE_IMPORT_DESCRIPTOR;
///
/// Debug Directory Format.
///
typedef struct {
UINT32 Characteristics;
UINT32 TimeDateStamp;
UINT16 MajorVersion;
UINT16 MinorVersion;
UINT32 Type;
UINT32 SizeOfData;
UINT32 RVA; ///< The address of the debug data when loaded, relative to the image base.
UINT32 FileOffset; ///< The file pointer to the debug data.
} EFI_IMAGE_DEBUG_DIRECTORY_ENTRY;
#define EFI_IMAGE_DEBUG_TYPE_CODEVIEW 2 ///< The Visual C++ debug information.
///
/// Debug Data Structure defined in Microsoft C++.
///
#define CODEVIEW_SIGNATURE_NB10 SIGNATURE_32('N', 'B', '1', '0')
typedef struct {
UINT32 Signature; ///< "NB10"
UINT32 Unknown;
UINT32 Unknown2;
UINT32 Unknown3;
//
// Filename of .PDB goes here
//
} EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY;
///
/// Debug Data Structure defined in Microsoft C++.
/// FIXME:
/// Documentation available at: https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
///
#define CODEVIEW_SIGNATURE_RSDS SIGNATURE_32('R', 'S', 'D', 'S')
typedef struct {
//
// 0x52 0x53 0x44 0x53 (ASCII string: "RSDS")
//
UINT32 Signature; ///< "RSDS".
//
// GUID (Globally Unique Identifier) of the associated PDB.
//
UINT8 Guid[16];
//
// Iteration of the PDB. The first iteration is 1. The iteration is
// incremented each time the PDB content is augmented.
//
UINT32 Age;
//
// UTF-8 NUL-terminated path to the associated .pdb file
//
} EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY;
///
/// Debug Data Structure defined by Apple Mach-O to Coff utility.
///
#define CODEVIEW_SIGNATURE_MTOC SIGNATURE_32('M', 'T', 'O', 'C')
typedef struct {
UINT32 Signature; ///< "MTOC".
UINT8 Uuid[16];
//
// Filename of .DLL (Mach-O with debug info) goes here
//
} EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY;
///
/// Resource directory entry format.
///
typedef struct {
union {
struct {
UINT32 NameOffset:31;
UINT32 NameIsString:1;
} s;
UINT32 Id;
} u1;
union {
UINT32 OffsetToData;
struct {
UINT32 OffsetToDirectory:31;
UINT32 DataIsDirectory:1;
} s;
} u2;
} EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY;
///
/// Resource format.
///
typedef struct {
UINT32 Characteristics;
UINT32 TimeDateStamp;
UINT16 MajorVersion;
UINT16 MinorVersion;
UINT16 NumberOfNamedEntries;
UINT16 NumberOfIdEntries;
EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY Entries[];
} EFI_IMAGE_RESOURCE_DIRECTORY;
///
/// Resource directory entry for string.
///
typedef struct {
UINT16 Length;
CHAR16 String[];
} EFI_IMAGE_RESOURCE_DIRECTORY_STRING;
///
/// Resource directory entry for data array.
///
typedef struct {
UINT32 OffsetToData;
UINT32 Size;
UINT32 CodePage;
UINT32 Reserved;
} EFI_IMAGE_RESOURCE_DATA_ENTRY;
///
/// Union of PE32 and PE32+ headers.
///
typedef union {
EFI_IMAGE_NT_HEADERS_COMMON_HDR PeCommon;
EFI_IMAGE_NT_HEADERS32 Pe32;
EFI_IMAGE_NT_HEADERS64 Pe32Plus;
} EFI_IMAGE_OPTIONAL_HEADER_UNION;
#endif // PE_COFF_IMAGE2_H_

View File

@ -0,0 +1,667 @@
/** @file
Definitions of the UEFI Executable (UE) file format.
Copyright (c) 2021 - 2023, Marvin Häuser. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef UE_IMAGE_H_
#define UE_IMAGE_H_
#include <Library/BaseLib.h>
//
// UE segment definitions.
//
///
/// Definition of the UE segment permission configurations.
///
enum {
UeSegmentPermX = 0,
UeSegmentPermRX = 1,
UeSegmentPermRW = 2,
//
// Read-only is the last value, as this makes it easier to implement it as
// the else/default case.
//
UeSegmentPermR = 3,
UeSegmentPermMax
};
///
/// The minimum alignment requirement, in bytes, of each UE segment in the UE
/// address space.
///
#define UE_SEGMENT_MIN_ALIGNMENT 0x00001000U
///
/// The maximum alignment requirement, in bytes, of each UE segment in the UE
/// address space.
///
#define UE_SEGMENT_MAX_ALIGNMENT 0x08000000U
///
/// Information about the UE segment in the UE address space.
///
/// [Bits 19:0] The size, in 4-KiB units, of the UE segment in the UE address
/// space.
/// [Bits 21:20] The UE segment permissions.
/// [Bits 31:22] Reserved for future use. Must be zero.
///
typedef UINT32 UE_SEGMENT_IMAGE_INFO;
///
/// Definition of a UE segment header.
///
typedef struct {
///
/// Information about the UE segment in the UE address space.
///
UE_SEGMENT_IMAGE_INFO ImageInfo;
///
/// The size, in bytes, of the UE segment in the UE file.
///
UINT32 FileSize;
} UE_SEGMENT;
STATIC_ASSERT (
sizeof (UE_SEGMENT) == 8 && ALIGNOF (UE_SEGMENT) == 4,
"The UE segment definition does not meet the specification."
);
///
/// Definition of a UE XIP segment header.
///
typedef struct {
///
/// Information about the UE segment in the UE address space.
///
UE_SEGMENT_IMAGE_INFO ImageInfo;
} UE_SEGMENT_XIP;
STATIC_ASSERT (
sizeof (UE_SEGMENT_XIP) == 4 && ALIGNOF (UE_SEGMENT_XIP) == 4,
"The UE XIP segment definition does not meet the specification."
);
/**
Retrieve the UE segment memory permissions.
@param[in] ImageInfo The UE segment image information.
**/
#define UE_SEGMENT_PERMISSIONS(ImageInfo) \
((UINT8)(((ImageInfo) >> 20U) & 0x03U))
/**
Retrieve the size, in bytes, of the UE segment in the UE address space.
@param[in] ImageInfo The UE segment image information.
**/
#define UE_SEGMENT_SIZE(ImageInfo) ((ImageInfo) << 12U)
STATIC_ASSERT (
IS_ALIGNED (UE_SEGMENT_SIZE (0xFFFFFFFF), UE_SEGMENT_MIN_ALIGNMENT),
"The UE segment size definition does not meet the specification."
);
//
// UE load table definitions.
//
///
/// The alignment, in bytes, of each UE load table in the UE file.
///
#define UE_LOAD_TABLE_ALIGNMENT 8U
///
/// Definition of the UE load table identifiers.
///
enum {
//
// An array of UE fixup roots. Blocks are ordered ascending by their
// base address.
//
UeLoadTableIdReloc = 0x00,
//
// An instance of the UE debug table..
//
UeLoadTableIdDebug = 0x01
};
///
/// Definition of a UE load table header.
///
typedef struct {
///
/// Information about the UE load table.
///
/// [Bits 28:0] The size, in 8-byte units, of the UE load table in the UE
/// file.
/// [Bits 31:29] The identifier of the UE load table.
///
UINT32 FileInfo;
} UE_LOAD_TABLE;
STATIC_ASSERT (
sizeof (UE_LOAD_TABLE) == 4 && ALIGNOF (UE_LOAD_TABLE) == 4,
"The UE load table definition does not meet the specification."
);
/**
Retrieves the UE load table identifier.
@param[in] FileInfo The UE load table file information.
**/
#define UE_LOAD_TABLE_ID(FileInfo) ((UINT8)((FileInfo) >> 29U))
/**
Retrieves the size, in bytes, of the UE load table in the UE file.
@param[in] FileInfo The UE load table file information.
**/
#define UE_LOAD_TABLE_SIZE(FileInfo) ((FileInfo) << 3U)
STATIC_ASSERT (
IS_ALIGNED (UE_LOAD_TABLE_SIZE (0xFFFFFFFF), UE_LOAD_TABLE_ALIGNMENT),
"The UE load table size definition does not meet the specification."
);
//
// UE relocation table definitions.
//
///
/// Definitions of the generic UE relocation identifiers.
///
enum {
UeReloc32 = 0x00,
UeReloc64 = 0x01,
UeReloc32NoMeta = 0x02,
UeRelocGenericMax
};
#if 0
///
/// Definition of the ARM UE relocation identifiers.
///
enum {
UeRelocArmMovtMovw = 0x02
};
#endif
///
/// The alignment requirement for a UE fixup root.
///
#define UE_FIXUP_ROOT_ALIGNMENT 4U
STATIC_ASSERT (
UE_FIXUP_ROOT_ALIGNMENT <= UE_LOAD_TABLE_ALIGNMENT,
"The UE fixup root definition does not meet the specification."
);
///
/// Definition of a UE fixup root.
///
typedef struct {
///
/// The offset of the first head fixup, in bytes, from the end of the previous
/// UE relocation fixup (chained or not). The first UE fixup root is
/// relative to 0.
///
UINT32 FirstOffset;
///
/// The head fixups of the UE fixup root.
///
/// [Bits 3:0] The type of the UE relocation fixup.
/// [Bits 15:4] The offset of the next UE head fixup from the end of the last
/// UE relocation fixup in the chain (if chained). If 0x0FFF, the
/// current fixup root is terminated.
///
UINT16 Heads[];
} UE_FIXUP_ROOT;
STATIC_ASSERT (
sizeof (UE_FIXUP_ROOT) == 4 && ALIGNOF (UE_FIXUP_ROOT) == UE_FIXUP_ROOT_ALIGNMENT,
"The UE fixup root definition does not meet the specification."
);
STATIC_ASSERT (
OFFSET_OF (UE_FIXUP_ROOT, Heads) == sizeof (UE_FIXUP_ROOT),
"The UE fixup root definition does not meet the specification."
);
STATIC_ASSERT (
sizeof (UE_FIXUP_ROOT) <= UE_LOAD_TABLE_ALIGNMENT,
"The UE fixup root definition is misaligned."
);
#define MIN_SIZE_OF_UE_FIXUP_ROOT (sizeof (UE_FIXUP_ROOT) + sizeof (UINT16))
///
/// The maximum offset, in bytes, of the next UE head fixup.
///
#define UE_HEAD_FIXUP_MAX_OFFSET 0x0FFEU
///
/// UE head fixup offset that terminates a fixup root.
///
#define UE_HEAD_FIXUP_OFFSET_END 0x0FFFU
/**
Retrieves the target offset of the UE relocation fixup.
@param[in] FixupInfo The UE relocation fixup information.
**/
#define UE_RELOC_FIXUP_OFFSET(FixupInfo) ((UINT16)((FixupInfo) >> 4U))
/**
Retrieves the type of the UE relocation fixup.
@param[in] FixupInfo The UE relocation fixup information.
**/
#define UE_RELOC_FIXUP_TYPE(FixupInfo) ((FixupInfo) & 0x000FU)
/**
Retrieves the offset of the next UE chained relocation fixup.
@param[in] FixupInfo The UE relocation fixup information.
**/
#define UE_CHAINED_RELOC_FIXUP_NEXT_OFFSET(FixupInfo) \
((UINT16)((UINT16)(FixupInfo) >> 4U) & 0x0FFFU)
///
/// The maximum offset, in bytes, of the next UE chained relocation fixup.
///
#define UE_CHAINED_RELOC_FIXUP_MAX_OFFSET 0x0FFEU
///
/// UE chained relocation fixup offset that terminates a chain.
///
#define UE_CHAINED_RELOC_FIXUP_OFFSET_END 0x0FFFU
/**
Retrieves the type of the next UE chained relocation fixup.
@param[in] FixupInfo The UE relocation fixup information.
**/
#define UE_CHAINED_RELOC_FIXUP_NEXT_TYPE(FixupInfo) \
((UINT8)((UINT16)(FixupInfo) & 0x0FU))
///
/// The shift exponent for UE chained relocation fixup values.
///
#define UE_CHAINED_RELOC_FIXUP_VALUE_SHIFT 16U
/**
Retrieves the value of the current UE chained relocation fixup.
@param[in] FixupInfo The UE relocation fixup information.
**/
#define UE_CHAINED_RELOC_FIXUP_VALUE(FixupInfo) \
RShiftU64 (FixupInfo, UE_CHAINED_RELOC_FIXUP_VALUE_SHIFT)
///
/// Definition of the common header of UE chained relocation fixups.
///
/// [Bits 3:0] The relocation type of the next chained relocation fixup. Only
/// valid when [Bits 15:4] are not 0x0FFF.
/// [Bits 15:4] The offset to the next chained relocation fixup from the end
/// of the current one. If 0x0FFF, the current chain is terminated.
/// Consult the fixup root for further relocation fixups.
///
typedef UINT16 UE_RELOC_FIXUP_HDR;
///
/// Definition of the generic 64-bit UE chained relocation fixup.
///
/// [Bits 15:0] The common header of UE chained relocation fixups.
/// [Bits 47:16] The address value to relocate.
/// [Bits 63:48] Must be zero.
///
typedef UINT64 UE_RELOC_FIXUP_64;
///
/// The shift exponent for UE chained 32-bit relocation fixup values.
///
#define UE_CHAINED_RELOC_FIXUP_VALUE_32_SHIFT 12U
/**
Retrieves the value of the current UE chained 32-bit relocation fixup.
@param[in] FixupInfo The UE relocation fixup information.
**/
#define UE_CHAINED_RELOC_FIXUP_VALUE_32(FixupInfo) \
(UINT32)((UINT32)(FixupInfo) >> UE_CHAINED_RELOC_FIXUP_VALUE_32_SHIFT)
///
/// Definition of the generic 32-bit UE chained relocation fixup.
///
/// [Bits 11:0] The offset to the next chained relocation fixup from the end
/// of the current one. If 0x0FFF, the current chain is terminated.
/// Consult the fixup root for further relocation fixups.
/// [Bits 31:12] The address value to relocate.
///
typedef UINT32 UE_RELOC_FIXUP_32;
#if 0
///
/// Definition of the ARM Thumb MOVT/MOVW UE chained relocation fixup.
///
/// [Bits 15:0] The common header of UE chained relocation fixups.
/// [Bits 31:16] The 16-bit immediate value to relocate.
///
typedef UINT32 UE_RELOC_FIXUP_ARM_MOVT_MOVW;
#endif
//
// UE debug table definitions.
//
// NOTE: The UE symbols base address offset is required for conversion of
// PE Images that have their first section start after the end of the
// Image headers. As PDBs cannot easily be rebased, store the offset.
//
///
/// Definition of a UE segment name. Must be \0-terminated.
///
typedef UINT8 UE_SEGMENT_NAME[8];
STATIC_ASSERT (
sizeof (UE_SEGMENT_NAME) == 8 && ALIGNOF (UE_SEGMENT_NAME) == 1,
"The UE segment name definition does not meet the specification."
);
///
/// Definition of the UE debug table header.
///
typedef struct {
///
/// Information about the image regarding the symbols file.
///
/// [Bits 1:0] The offset, in image alignment units, to be subtracted from the
/// UE base address in order to retrieve the UE symbols base
/// address.
/// [Bits 7:2] Reserved for future use. Must be zero.
///
UINT8 ImageInfo;
///
/// The length, in bytes, of the UE symbols path (excluding the terminator).
///
UINT8 SymbolsPathLength;
///
/// The UE symbols path. Must be \0-terminated.
///
UINT8 SymbolsPath[];
///
/// The UE segment name table. The order matches the UE segment table.
///
//UE_SEGMENT_NAME SegmentNames[];
} UE_DEBUG_TABLE;
///
/// The minimum size, in bytes, of the UE debug table.
///
#define MIN_SIZE_OF_UE_DEBUG_TABLE \
(OFFSET_OF (UE_DEBUG_TABLE, SymbolsPath) + 1U)
/**
Retrieves the UE symbol address subtrahend in SegmentAlignment-units.
@param[in] ImageInfo The UE debug table image information.
**/
#define UE_DEBUG_TABLE_IMAGE_INFO_SYM_SUBTRAHEND_FACTOR(ImageInfo) \
((UINT8)((ImageInfo) & 0x03U))
/**
Retrieves the UE segment name table of a UE debug table.
@param[in] DebugTable The UE debug table.
**/
#define UE_DEBUG_TABLE_SEGMENT_NAMES(DebugTable) \
(CONST UE_SEGMENT_NAME *) ( \
(DebugTable)->SymbolsPath + (DebugTable)->SymbolsPathLength + 1 \
)
STATIC_ASSERT (
sizeof (UE_DEBUG_TABLE) == 2 && ALIGNOF (UE_DEBUG_TABLE) == 1,
"The UE debug table definition does not meet the specification."
);
STATIC_ASSERT (
ALIGNOF (UE_DEBUG_TABLE) <= UE_LOAD_TABLE_ALIGNMENT,
"The UE debug table definition is misaligned."
);
STATIC_ASSERT (
OFFSET_OF (UE_DEBUG_TABLE, SymbolsPath) == sizeof (UE_DEBUG_TABLE),
"The UE fixup root definition does not meet the specification."
);
//
// UE header definitions.
//
///
/// The file magic number of a UE header.
///
#define UE_HEADER_MAGIC SIGNATURE_16 ('U', 'E')
///
/// Definition of the UE machine identifiers.
///
enum {
UeMachineI386 = 0,
UeMachineX64 = 1,
UeMachineArmThumbMixed = 2,
UeMachineArm64 = 3,
UeMachineRiscV32 = 4,
UeMachineRiscV64 = 5,
UeMachineRiscV128 = 6
};
///
/// Definition of the UE subsystem identifiers.
///
enum {
UeSubsystemEfiApplication = 0,
UeSubsystemEfiBootServicesDriver = 1,
UeSubsystemEfiRuntimeDriver = 2
};
///
/// Definition of a UE file header.
///
typedef struct {
///
/// The file magic number to identify the UE file format. Must be 'UE'.
///
UINT16 Magic;
///
/// Information about the image kind and supported architectures.
///
/// [Bits 2:0] Indicates the subsystem.
/// [Bits 7:3] Indicates the supported architectures.
///
UINT8 Type;
///
/// Information about the UE load tables and segments.
///
/// [Bits 2:0] The number of UE load tables.
/// [Bits 7:3] The index of the last segment in the UE segment table.
///
UINT8 TableCounts;
///
/// Indicates the offset of the UE entry point in the UE address space.
///
UINT32 EntryPointAddress;
///
/// Information about the UE image.
///
/// [Bits 51:0] The base UEFI page of the UE image, i.e., the base address in
/// 4 KiB units.
/// [Bits 55:52] Reserved for future use. Must be zero.
/// [Bit 56] Indicates whether the UE image is XIP
/// [Bit 57] Indicates whether the UE image is designated for a fixed
/// address.
/// [Bit 58] Indicates whether the UE relocation table has been stripped.
/// [Bit 59] Indicates whether UE chained fixups are used.
/// [Bits 63:60] The shift exponent, offset by -12, for the UE segment
/// alignment in bytes.
///
UINT64 ImageInfo;
///
/// The UE segment table. It contains all data of the UE address space.
///
/// All UE segments are contiguous in the UE address space.
/// The offset of the first UE segment in the UE address space is 0.
///
/// All UE segments' data are contiguous in the UE file.
/// The offset of the first UE segment in the UE file is the end of the UE
/// file header.
///
UE_SEGMENT Segments[];
///
/// The UE load tables. They contain data useful for UE loading.
///
/// All UE load tables are contiguous in the UE file.
/// The offset of the first UE load table in the UE file is the end of the last
/// UE segment in the UE file.
///
/// All UE load tables are ordered ascending by their identifier.
///
//UE_LOAD_TABLE LoadTables[];
} UE_HEADER;
///
/// The minimum size, in bytes, of a valid UE header.
///
#define MIN_SIZE_OF_UE_HEADER \
(OFFSET_OF (UE_HEADER, Segments) + sizeof (UE_SEGMENT))
STATIC_ASSERT (
sizeof (UE_HEADER) == 16 && ALIGNOF (UE_HEADER) == 8,
"The UE header definition does not meet the specification."
);
STATIC_ASSERT (
ALIGNOF (UE_SEGMENT) <= ALIGNOF (UE_LOAD_TABLE),
"The UE header definition is misaligned."
);
STATIC_ASSERT (
OFFSET_OF (UE_HEADER, Segments) == sizeof (UE_HEADER),
"The UE header definition does not meet the specification."
);
/**
Retrieves the UE base address.
@param[in] ImageInfo The UE header image information.
**/
#define UE_HEADER_BASE_ADDRESS(ImageInfo) LShiftU64 (ImageInfo, 12)
/**
Retrieves the UE segment alignment, in bytes, as a power of two.
@param[in] ImageInfo The UE header image information.
**/
#define UE_HEADER_SEGMENT_ALIGNMENT(ImageInfo) \
(1U << ((UINT8)RShiftU64 (ImageInfo, 60) + 12U))
///
/// UE header image information bit that indicates whether the image is XIP.
///
#define UE_HEADER_IMAGE_INFO_XIP 0x0100000000000000ULL
///
/// UE header image information bit that indicates whether the image is
/// designated to be loaded to a fixed address.
///
#define UE_HEADER_IMAGE_INFO_FIXED_ADDRESS 0x0200000000000000ULL
///
/// UE header image information bit that indicates whether the relocation fixups
/// have been stripped.
///
#define UE_HEADER_IMAGE_INFO_RELOCATION_FIXUPS_STRIPPED 0x0400000000000000ULL
///
/// UE header image information bit that indicates whether UE relocation fixup
/// chains are utilized.
///
#define UE_HEADER_IMAGE_INFO_CHAINED_FIXUPS 0x0800000000000000ULL
/**
Retrieves the UE subsystem.
@param[in] Type The UE header type information.
**/
#define UE_HEADER_SUBSYSTEM(Type) ((Type) & 0x07U)
/**
Retrieves the UE supported architectures.
@param[in] Type The UE header type information.
**/
#define UE_HEADER_ARCH(Type) ((Type) >> 3U)
///
/// The maximum number of UE load tables.
///
#define UE_HEADER_NUM_LOAD_TABLES_MAX 7U
/**
Retrieves the number of UE load tables.
@param[in] TableCounts The UE header segment and load table information.
**/
#define UE_HEADER_NUM_LOAD_TABLES(TableCounts) ((TableCounts) & 0x07U)
STATIC_ASSERT (
UE_HEADER_NUM_LOAD_TABLES (0xFFU) == UE_HEADER_NUM_LOAD_TABLES_MAX,
"The number of load tables violates the specification."
);
///
/// The maximum number of UE segments.
///
#define UE_HEADER_NUM_SEGMENTS_MAX 32U
/**
Retrieves the index of the last UE segment, i.e., their amount minus 1.
@param[in] TableCounts The UE header segment and load table information.
**/
#define UE_HEADER_LAST_SEGMENT_INDEX(TableCounts) ((TableCounts) >> 3U)
STATIC_ASSERT (
UE_HEADER_LAST_SEGMENT_INDEX (0xFFU) + 1U == UE_HEADER_NUM_SEGMENTS_MAX,
"The number of load tables violates the specification."
);
/**
Retrieves the 8 byte aligned UE file size.
If the file size is larger than this value, the appended data may be the UE
certificate table.
@param[in] FileInfo The UE header file information.
**/
#define UE_HEADER_FILE_SIZE(FileInfo) ((FileInfo) << 3U)
STATIC_ASSERT (
IS_ALIGNED (UE_HEADER_FILE_SIZE (0xFFFFFFFF), UE_LOAD_TABLE_ALIGNMENT),
"The UE file size definition does not meet the specification."
);
///
/// The maximum size, in bytes, of a valid UE header.
///
#define MAX_SIZE_OF_UE_HEADER \
MIN_SIZE_OF_UE_HEADER + \
UE_HEADER_NUM_SEGMENTS_MAX * sizeof (UE_SEGMENT) + \
UE_HEADER_NUM_LOAD_TABLES_MAX * sizeof (UE_LOAD_TABLE)
#endif // UE_IMAGE_H_

View File

@ -3028,6 +3028,34 @@ PathCleanUpDirectories(
#define INITIALIZE_LIST_HEAD_VARIABLE(ListHead) {&(ListHead), &(ListHead)}
/**
Iterates over each node in a doubly linked list using each node's forward link.
@param Entry A pointer to a list node used as a loop cursor during iteration
@param ListHead The head node of the doubly linked list
**/
#define BASE_LIST_FOR_EACH(Entry, ListHead) \
for(Entry = (ListHead)->ForwardLink; Entry != (ListHead); Entry = Entry->ForwardLink)
/**
Iterates over each node in a doubly linked list using each node's forward link
with safety against node removal.
This macro uses NextEntry to temporarily store the next list node so the node
pointed to by Entry may be deleted in the current loop iteration step and
iteration can continue from the node pointed to by NextEntry.
@param Entry A pointer to a list node used as a loop cursor during iteration
@param NextEntry A pointer to a list node used to temporarily store the next node
@param ListHead The head node of the doubly linked list
**/
#define BASE_LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead) \
for(Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink;\
Entry != (ListHead); Entry = NextEntry, NextEntry = Entry->ForwardLink)
/**
Checks whether FirstEntry and SecondEntry are part of the same doubly-linked
list.
@ -5065,6 +5093,40 @@ CalculateCrc32(
IN UINTN Length
);
/**
Calculates the CRC16-ANSI checksum of the given buffer.
@param[in] Buffer Pointer to the buffer.
@param[in] Length Length of the buffer, in bytes.
@param[in] InitialValue Initial value of the CRC.
@return The CRC16-ANSI checksum.
**/
UINT16
EFIAPI
CalculateCrc16Ansi (
IN CONST VOID *Buffer,
IN UINTN Length,
IN UINT16 InitialValue
);
/**
Calculates the CRC32c checksum of the given buffer.
@param[in] Buffer Pointer to the buffer.
@param[in] Length Length of the buffer, in bytes.
@param[in] InitialValue Initial value of the CRC.
@return The CRC32c checksum.
**/
UINT32
EFIAPI
CalculateCrc32c (
IN CONST VOID *Buffer,
IN UINTN Length,
IN UINT32 InitialValue
);
//
// Base Library CPU Functions
//

View File

@ -0,0 +1,445 @@
/** @file
BaseOverflowLib
Copyright (c) 2018, vit9696
All rights reserved.
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.
**/
#ifndef __BASE_OVERFLOW_LIB__
#define __BASE_OVERFLOW_LIB__
//
// The macros below provide pointer alignment checking interfaces.
// TypedPtr - pointer of a dedicated type, which alignment is to be checked.
// Align - valid alignment for the target platform (power of two so far).
// Type - valid complete typename.
// Ptr - raw pointer value, must fit into UINTN, meant to be uintptr_t equivalent.
//
#define BASE_ALIGNOF(Type) (_Alignof (Type))
#define BASE_POT_ALIGNED(Align, Ptr) (0ULL == (((UINTN) (Ptr)) & (Align-1U)))
#define BASE_TYPE_ALIGNED(Type, Ptr) (BASE_POT_ALIGNED (BASE_ALIGNOF (Type), Ptr))
//
// Force member alignment for the structure.
//
#if (defined (__STDC__) && __STDC_VERSION__ >= 201112L) || defined (__GNUC__) || defined (__clang__)
#define BASE_ALIGNAS(Alignment) _Alignas(Alignment)
#else
#define BASE_ALIGNAS(Alignment)
#endif
/**
Return the result of (Multiplicand * Multiplier / Divisor).
@param Multiplicand A 64-bit unsigned value.
@param Multiplier A 64-bit unsigned value.
@param Divisor A 32-bit unsigned value.
@param Remainder A pointer to a 32-bit unsigned value. This parameter is
optional and may be NULL.
@return Multiplicand * Multiplier / Divisor.
**/
UINT64
BaseMultThenDivU64x64x32 (
IN UINT64 Multiplicand,
IN UINT64 Multiplier,
IN UINT32 Divisor,
OUT UINT32 *Remainder OPTIONAL
);
//
// The interfaces below provide base safe arithmetics, reporting
// signed integer overflow and unsigned integer wraparound similarly to
// os/overflow.h in macOS SDK.
//
// Each interface may be implemented not only as an actual function, but
// a macro as well. Macro implementations are allowed to evaluate the
// expressions no more than once, and are supposed to provide faster
// compiler builtins if available.
//
// Each interface returns FALSE when the the stored result is equal to
// the infinite precision result, otherwise TRUE. The operands should
// be read left to right with the last argument representing a non-NULL
// pointer to the resulting value of the same type.
//
// More information could be found in Clang Extensions documentation:
// http://releases.llvm.org/7.0.0/tools/clang/docs/LanguageExtensions.html#checked-arithmetic-builtins
//
//
// 32-bit integer addition, subtraction, multiplication, triple addition (A+B+C),
// triple multiplication (A*B*C), addition with multiplication ((A+B)*C),
// and multiplication with addition (A*B+C) support.
//
BOOLEAN
BaseOverflowAddU16 (
UINT16 A,
UINT16 B,
UINT16 *Result
);
BOOLEAN
BaseOverflowSubU16 (
UINT16 A,
UINT16 B,
UINT16 *Result
);
BOOLEAN
BaseOverflowMulU16 (
UINT16 A,
UINT16 B,
UINT16 *Result
);
BOOLEAN
BaseOverflowAddU32 (
UINT32 A,
UINT32 B,
UINT32 *Result
);
BOOLEAN
BaseOverflowSubU32 (
UINT32 A,
UINT32 B,
UINT32 *Result
);
BOOLEAN
BaseOverflowMulU32 (
UINT32 A,
UINT32 B,
UINT32 *Result
);
BOOLEAN
BaseOverflowTriAddU32 (
UINT32 A,
UINT32 B,
UINT32 C,
UINT32 *Result
);
BOOLEAN
BaseOverflowTriMulU32 (
UINT32 A,
UINT32 B,
UINT32 C,
UINT32 *Result
);
BOOLEAN
BaseOverflowAddMulU32 (
UINT32 A,
UINT32 B,
UINT32 C,
UINT32 *Result
);
BOOLEAN
BaseOverflowMulAddU32 (
UINT32 A,
UINT32 B,
UINT32 C,
UINT32 *Result
);
BOOLEAN
BaseOverflowAlignUpU32 (
UINT32 Value,
UINT32 Alignment,
UINT32 *Result
);
BOOLEAN
BaseOverflowAddS32 (
INT32 A,
INT32 B,
INT32 *Result
);
BOOLEAN
BaseOverflowSubS32 (
INT32 A,
INT32 B,
INT32 *Result
);
BOOLEAN
BaseOverflowMulS32 (
INT32 A,
INT32 B,
INT32 *Result
);
BOOLEAN
BaseOverflowTriAddS32 (
INT32 A,
INT32 B,
INT32 C,
INT32 *Result
);
BOOLEAN
BaseOverflowTriMulS32 (
INT32 A,
INT32 B,
INT32 C,
INT32 *Result
);
BOOLEAN
BaseOverflowAddMulS32 (
INT32 A,
INT32 B,
INT32 C,
INT32 *Result
);
BOOLEAN
BaseOverflowMulAddS32 (
INT32 A,
INT32 B,
INT32 C,
INT32 *Result
);
//
// 64-bit integer addition, subtraction, multiplication, triple addition (A+B+C),
// triple multiplication (A*B*C), addition with multiplication ((A+B)*C),
// and multiplication with addition (A*B+C) support.
//
BOOLEAN
BaseOverflowAddU64 (
UINT64 A,
UINT64 B,
UINT64 *Result
);
BOOLEAN
BaseOverflowSubU64 (
UINT64 A,
UINT64 B,
UINT64 *Result
);
BOOLEAN
BaseOverflowMulU64 (
UINT64 A,
UINT64 B,
UINT64 *Result
);
BOOLEAN
BaseOverflowTriAddU64 (
UINT64 A,
UINT64 B,
UINT64 C,
UINT64 *Result
);
BOOLEAN
BaseOverflowTriMulU64 (
UINT64 A,
UINT64 B,
UINT64 C,
UINT64 *Result
);
BOOLEAN
BaseOverflowAddMulU64 (
UINT64 A,
UINT64 B,
UINT64 C,
UINT64 *Result
);
BOOLEAN
BaseOverflowMulAddU64 (
UINT64 A,
UINT64 B,
UINT64 C,
UINT64 *Result
);
BOOLEAN
BaseOverflowAddS64 (
INT64 A,
INT64 B,
INT64 *Result
);
BOOLEAN
BaseOverflowSubS64 (
INT64 A,
INT64 B,
INT64 *Result
);
BOOLEAN
BaseOverflowMulS64 (
INT64 A,
INT64 B,
INT64 *Result
);
BOOLEAN
BaseOverflowTriAddS64 (
INT64 A,
INT64 B,
INT64 C,
INT64 *Result
);
BOOLEAN
BaseOverflowTriMulS64 (
INT64 A,
INT64 B,
INT64 C,
INT64 *Result
);
BOOLEAN
BaseOverflowAddMulS64 (
INT64 A,
INT64 B,
INT64 C,
INT64 *Result
);
BOOLEAN
BaseOverflowMulAddS64 (
INT64 A,
INT64 B,
INT64 C,
INT64 *Result
);
//
// Native integer addition, subtraction, multiplication, triple addition (A+B+C),
// triple multiplication (A*B*C), addition with multiplication ((A+B)*C),
// and multiplication with addition (A*B+C) support.
//
BOOLEAN
BaseOverflowAddUN (
UINTN A,
UINTN B,
UINTN *Result
);
BOOLEAN
BaseOverflowSubUN (
UINTN A,
UINTN B,
UINTN *Result
);
BOOLEAN
BaseOverflowMulUN (
UINTN A,
UINTN B,
UINTN *Result
);
BOOLEAN
BaseOverflowTriAddUN (
UINTN A,
UINTN B,
UINTN C,
UINTN *Result
);
BOOLEAN
BaseOverflowTriMulUN (
UINTN A,
UINTN B,
UINTN C,
UINTN *Result
);
BOOLEAN
BaseOverflowAddMulUN (
UINTN A,
UINTN B,
UINTN C,
UINTN *Result
);
BOOLEAN
BaseOverflowMulAddUN (
UINTN A,
UINTN B,
UINTN C,
UINTN *Result
);
BOOLEAN
BaseOverflowAddSN (
INTN A,
INTN B,
INTN *Result
);
BOOLEAN
BaseOverflowSubSN (
INTN A,
INTN B,
INTN *Result
);
BOOLEAN
BaseOverflowMulSN (
INTN A,
INTN B,
INTN *Result
);
BOOLEAN
BaseOverflowTriAddSN (
INTN A,
INTN B,
INTN C,
INTN *Result
);
BOOLEAN
BaseOverflowTriMulSN (
INTN A,
INTN B,
INTN C,
INTN *Result
);
BOOLEAN
BaseOverflowAddMulSN (
INTN A,
INTN B,
INTN C,
INTN *Result
);
BOOLEAN
BaseOverflowMulAddSN (
INTN A,
INTN B,
INTN C,
INTN *Result
);
#endif // __BASE_OVERFLOW_LIB__

View File

@ -16,6 +16,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#ifndef __DEBUG_LIB_H__
#define __DEBUG_LIB_H__
#include <Library/BaseLib.h> // jief : for CpuBreakpoint() definition
//
// Declare bits for PcdDebugPropertyMask
//
@ -585,6 +586,20 @@ DebugPrintLevelEnabled (
} \
} while (FALSE)
#define DEBUG_RAISE() \
do { \
if ((PcdGet8 (PcdDebugRaisePropertyMask) & DEBUG_PROPERTY_DEBUG_PRINT_ENABLED) != 0) { \
DEBUG ((DEBUG_WARN, "DEBUG RAISE: Constraint violation in %a:%a:%u\n", __FILE__, __func__, __LINE__));\
} \
\
if ((PcdGet8 (PcdDebugRaisePropertyMask) & DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED) != 0) { \
ASSERT (FALSE); \
} \
\
if ((PcdGet8 (PcdDebugRaisePropertyMask) & DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED) != 0) { \
CpuBreakpoint (); \
} \
} while (FALSE)
/**
Macro that calls DebugAssert() if the containing record does not have a

View File

@ -0,0 +1,75 @@
/** @file
Provides extended services to allocate and free memory buffers of various memory types and alignments.
This header is part of the MemoryAllocationLib library class. Every instance
of MemoryAllocationLib must also implement the functions declared by this
header. The separation is due to the newly-introduced dependency on
MdePkg/Uefi/UefiSpec.h.
Copyright (c) 2023, Marvin Häuser. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef __MEMORY_ALLOCATION_LIB_EX_H__
#define __MEMORY_ALLOCATION_LIB_EX_H__
/**
Allocates one or more 4KB pages of a certain memory type.
Allocates the number of 4KB pages of a certain memory type and returns a pointer
to the allocated buffer. The buffer returned is aligned on a 4KB boundary.
@param Type The type of allocation to perform.
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@param Memory The pointer to a physical address. On input, the
way in which the address is used depends on the
value of Type.
@retval EFI_SUCCESS The requested pages were allocated.
@retval EFI_OUT_OF_RESOURCES The pages could not be allocated.
@retval EFI_NOT_FOUND The requested pages could not be found.
**/
EFI_STATUS
EFIAPI
AllocatePagesEx (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN OUT EFI_PHYSICAL_ADDRESS *Memory
);
/**
Allocates one or more 4KB pages of a certain memory type at a specified alignment.
Allocates the number of 4KB pages specified by Pages of a certain memory type with an alignment
specified by Alignment.
If Alignment is not a power of two and Alignment is not zero, then ASSERT().
If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
@param Type The type of allocation to perform.
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@param Alignment The requested alignment of the allocation. Must be a power of two.
If Alignment is zero, then byte alignment is used.
@param Memory The pointer to a physical address. On input, the
way in which the address is used depends on the
value of Type.
@retval EFI_SUCCESS The requested pages were allocated.
@retval EFI_OUT_OF_RESOURCES The pages could not be allocated.
@retval EFI_NOT_FOUND The requested pages could not be found.
**/
EFI_STATUS
EFIAPI
AllocateAlignedPagesEx (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN UINTN Alignment,
IN OUT EFI_PHYSICAL_ADDRESS *Memory
);
#endif

View File

@ -0,0 +1,590 @@
/** @file
Provides APIs to inspect, load, and relocate PE/COFF Images.
No implementation of this library may use global variable pointers, as this
may cause the emission of Image relocations for this address. This is
incompatible with the concept of Image self-relocation, where the Image is
loaded in a similar fashion to XIP Images into the memory at an address
unknown at compile-time. As such, Image relocation must be safe to perform
without any Image relocations applied earlier.
Copyright (c) 2020 - 2021, Marvin Häuser. All rights reserved.<BR>
Copyright (c) 2020, Vitaly Cheptsov. All rights reserved.<BR>
Copyright (c) 2020, ISP RAS. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#ifndef PE_COFF_LIB2_H_
#define PE_COFF_LIB2_H_
#include <IndustryStandard/PeImage2.h>
#include <Guid/WinCertificate.h>
typedef enum {
UefiImageOriginFv = 0,
UefiImageOriginOptionROM = 1,
UefiImageOriginUserImage = 2,
UefiImageOriginMax
} UEFI_IMAGE_ORIGIN;
///
/// If set, less than 4KB aligned image from firmware volume prevents boot.
///
#define PCD_IMAGE_PROTECTION_POLICY_FV_STOP_BOOT BIT31
// FIXME: Where to put this?
//
// PcdImageLoaderAlignmentPolicy bits.
//
///
/// If set, unaligned Image sections are permitted.
///
#define PCD_ALIGNMENT_POLICY_CONTIGUOUS_SECTIONS BIT0
///
/// If set, unaligned Image Relocation Block sizes are permitted.
///
#define PCD_ALIGNMENT_POLICY_RELOCATION_BLOCK_SIZES BIT1
///
/// If set, unaligned Image certificate sizes are permitted.
///
#define PCD_ALIGNMENT_POLICY_CERTIFICATE_SIZES BIT2
// FIXME: Rework docs to consider Inplace dependencies
/**
Returns whether the Image targets the UEFI Subsystem.
@param[in] Subsystem The Subsystem value from the Image Headers.
**/
#define IMAGE_IS_EFI_SUBYSYSTEM(Subsystem) \
((Subsystem) >= EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION && \
(Subsystem) <= EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER)
///
/// Image type enumeration for Image format identification from the context.
///
typedef enum {
PeCoffLoaderTypePe32,
PeCoffLoaderTypePe32Plus,
PeCoffLoaderTypeMax
} PE_COFF_LOADER_IMAGE_TYPE;
///
/// Image context structure used for abstraction and bookkeeping.
/// This structure is publicly exposed for memory allocation reasons and must
/// not be accessed directly outside of the library implementation.
///
typedef struct {
///
/// The preferred load address of the Image.
///
UINT64 ImageBase;
///
/// A pointer to the Image raw file buffer.
///
CONST VOID *FileBuffer;
///
/// The size, in Bytes, of FileBuffer.
///
UINT32 FileSize;
///
/// A pointer to the loaded Image destination.
///
VOID *ImageBuffer;
///
/// The offset of the Section Headers from the beginning of the raw file.
///
UINT32 SectionsOffset;
///
/// The number of Sections in the Image.
///
UINT16 NumberOfSections;
///
/// The size, in Bytes, required to load the Image.
///
UINT32 SizeOfImage;
///
/// The alignment, in Bytes, of Image Sections virtual addresses.
///
UINT32 SectionAlignment;
///
/// The offset of the Image Header from the beginning of the raw file.
///
UINT32 ExeHdrOffset;
///
/// The combined size, in Bytes, of all Image Headers.
///
UINT32 SizeOfHeaders;
///
/// The RVA of the Image entry point.
///
UINT32 AddressOfEntryPoint;
///
/// Indicates whether relocation information has been stripped from the Image.
///
BOOLEAN RelocsStripped;
///
/// The file format of the Image raw file, refer to PE_COFF_LOADER_IMAGE_TYPE.
///
UINT8 ImageType;
///
/// The Subsystem value from the Image Header.
///
UINT16 Subsystem;
///
/// The Machine value from the Image Header.
///
UINT16 Machine;
///
/// The RVA of the Relocation Directory.
///
UINT32 RelocDirRva;
///
/// The size, in Bytes, of the Relocation Directory.
///
UINT32 RelocDirSize;
///
/// The RVA of the Security Directory.
///
UINT32 SecDirOffset;
///
/// The size, in Bytes, of the Security Directory.
///
UINT32 SecDirSize;
} PE_COFF_LOADER_IMAGE_CONTEXT;
///
/// Image runtime context used to relocate the Image during runtime.
///
typedef struct PE_COFF_LOADER_RUNTIME_CONTEXT_ PE_COFF_LOADER_RUNTIME_CONTEXT;
/**
Adds the digest of Data to HashContext. This function can be called multiple
times to compute the digest of discontinuous data.
@param[in,out] HashContext The context of the current hash.
@param[in] Data The data to be hashed.
@param[in] DataSize The size, in Bytes, of Data.
@returns Whether hashing has been successful.
**/
typedef
BOOLEAN
(EFIAPI *PE_COFF_LOADER_HASH_UPDATE)(
IN OUT VOID *HashContext,
IN CONST VOID *Data,
IN UINTN DataSize
);
/**
Verify the TE, PE32, or PE32+ Image and initialise Context.
Used offsets and ranges must be aligned and in the bounds of the raw file.
Image Section Headers and basic Relocation information must be well-formed.
FileBuffer must remain valid for the entire lifetime of Context.
@param[out] Context The context describing the Image.
@param[in] FileBuffer The file data to parse as PE Image.
@param[in] FileSize The size, in Bytes, of FileBuffer.
@retval RETURN_SUCCESS The Image context has been initialised successfully.
@retval other The file data is malformed.
**/
RETURN_STATUS
PeCoffInitializeContext (
OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
IN CONST VOID *FileBuffer,
IN UINT32 FileSize,
IN UINT8 ImageOrigin
);
/**
Hashes the Image using the Authenticode (PE/COFF Specification 8.1 Appendix A)
algorithm.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@param[in,out] HashContext The context of the current hash. Must have been
initialised for usage with the HashUpdate
function.
@param[in] HashUpdate The data hashing function.
@returns Whether hashing has been successful.
**/
BOOLEAN
PeCoffHashImageAuthenticode (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
IN OUT VOID *HashContext,
IN PE_COFF_LOADER_HASH_UPDATE HashUpdate
);
/**
Load the Image into the destination memory space.
@param[in,out] Context The context describing the Image. Must have
been initialised by PeCoffInitializeContext().
@param[out] Destination The Image destination memory. Must be
allocated from page memory.
@param[in] DestinationSize The size, in Bytes, of Destination. Must be
sufficent to load the Image with regards to
its Image section alignment.
@retval RETURN_SUCCESS The Image was loaded successfully.
@retval other The Image could not be loaded successfully.
**/
RETURN_STATUS
PeCoffLoadImage (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
OUT VOID *Destination,
IN UINT32 DestinationSize
);
// FIXME: Docs
BOOLEAN
PeCoffImageIsInplace (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Equivalent to the PeCoffLoadImage() function for inplace-loading. Ensures that
all important raw file offsets match the respective RVAs.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@retval RETURN_SUCCESS The Image has been inplace-loaded successfully.
@retval other The Image is not suitable for inplace-loading.
**/
RETURN_STATUS
PeCoffLoadImageInplace (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
// FIXME: Docs
RETURN_STATUS
PeCoffRelocateImageInplace (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the size required to bookkeep Image runtime relocation information.
May only be called when PeCoffGetRelocsStripped() returns FALSE.
@param[in,out] Context The context describing the Image. Must have been
loaded by PeCoffLoadImage().
@param[out] Size On output, the size, in Bytes, required for the
bookkeeping buffer.
@retval RETURN_SUCCESS The Image runtime context size for the Image was
retrieved successfully.
@retval other The Image runtime context size for the Image could not
be retrieved successfully.
**/
RETURN_STATUS
PeCoffLoaderGetRuntimeContextSize (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
OUT UINT32 *Size
);
/**
Relocate the Image for boot-time usage.
May only be called when PeCoffGetRelocsStripped() returns FALSE, or with
BaseAddress == PeCoffGetImageBase().
@param[in,out] Context The context describing the Image. Must have
been loaded by PeCoffLoadImage().
@param[in] BaseAddress The address to relocate the Image to.
@param[out] RuntimeContext If not NULL, on output, a bookkeeping data
required for Image runtime relocation.
@param[in] RuntimeContextSize The size, in Bytes, of RuntimeContext. Must
be at least as big as the size returned by
PeCoffLoaderGetRuntimeContextSize().
@retval RETURN_SUCCESS The Image has been relocated successfully.
@retval other The Image Relocation Directory is malformed.
**/
RETURN_STATUS
PeCoffRelocateImage (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
IN UINT64 BaseAddress,
OUT PE_COFF_LOADER_RUNTIME_CONTEXT *RuntimeContext OPTIONAL,
IN UINT32 RuntimeContextSize
);
/**
Relocate Image for Runtime usage.
May only be called when PeCoffGetRelocsStripped() returns FALSE, or with
BaseAddress == PeCoffGetImageBase().
@param[in,out] Image The Image destination memory. Must have been
relocated by PeCoffRelocateImage().
@param[in] ImageSize The size, in Bytes, of Image.
@param[in] BaseAddress The address to relocate the Image to.
@param[in] RuntimeContext The Relocation context obtained by
PeCoffRelocateImage().
@retval RETURN_SUCCESS The Image has been relocated successfully.
@retval other The Image could not be relocated successfully.
**/
RETURN_STATUS
PeCoffRuntimeRelocateImage (
IN OUT VOID *Image,
IN UINT32 ImageSize,
IN UINT64 BaseAddress,
IN CONST PE_COFF_LOADER_RUNTIME_CONTEXT *RuntimeContext
);
/**
Discards optional Image Sections to disguise sensitive data.
This may destruct the Image Relocation Directory and as such, no function that
performs Image relocation may be called after this function has been invoked.
@param[in,out] Context The context describing the Image. Must have been
loaded by PeCoffLoadImage().
**/
VOID
PeCoffDiscardSections (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image PDB path.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@param[out] PdbPath On output, a pointer to the Image PDB path.
@param[out] PdbPathSize On output, the size, in Bytes, of *PdbPath.
@retval RETURN_SUCCESS The Image PDB path was retrieved successfully.
@retval other The Image PDB path could not be retrieved
successfully.
**/
RETURN_STATUS
PeCoffGetPdbPath (
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
OUT CONST CHAR8 **PdbPath,
OUT UINT32 *PdbPathSize
);
/**
Retrieves the first certificate from the Image Certificate Directory.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@param[out] Certificate On output, the first certificate of the Image.
@retval RETURN_SUCCESS The certificate has been retrieved successfully.
@retval RETURN_NOT_FOUND There is no such certificate.
@retval other The Image Certificate Directory is malformed.
**/
RETURN_STATUS
PeCoffGetFirstCertificate (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
OUT CONST WIN_CERTIFICATE **Certificate
);
/**
Retrieves the next certificate from the Image Certificate Directory.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@param[out] Certificate On input, the current certificate of the Image.
Must have been retrieved by
PeCoffGetFirstCertificate().
On output, the next certificate of the Image.
@retval RETURN_SUCCESS The certificate has been retrieved successfully.
@retval RETURN_NOT_FOUND There is no such certificate.
@retval other The Image Certificate Directory is malformed.
**/
RETURN_STATUS
PeCoffGetNextCertificate (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
IN OUT CONST WIN_CERTIFICATE **Certificate
);
/**
Retrieves the Image Section Table.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@param[out] Sections On output, points to the Image Section Table.
@returns The number of sections in the Image Section Table.
**/
UINT16
PeCoffGetSectionTable (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
OUT CONST EFI_IMAGE_SECTION_HEADER **Sections
);
/**
Retrieves the Image HII data RVA.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@param[out] HiiRva On output, the RVA of the HII resource data.
@param[out] HiiSize On output, the size, in Bytes, of HiiRva.
@retval RETURN_SUCCESS The Image HII data has been retrieved successfully.
@retval RETURN_NOT_FOUND The Image HII data could not be found.
@retval other The Image Resource Directory is malformed.
**/
RETURN_STATUS
PeCoffGetHiiDataRva (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
OUT UINT32 *HiiRva,
OUT UINT32 *HiiSize
);
/**
Retrieve the Image entry point RVA.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns The Image entry point RVA.
**/
UINT32
PeCoffGetAddressOfEntryPoint (
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image machine type.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns The Image machine type.
**/
UINT16
PeCoffGetMachine (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image subsystem type.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns The Image subsystem type.
**/
UINT16
PeCoffGetSubsystem (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image section alignment.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns The Image section alignment.
**/
UINT32
PeCoffGetSectionAlignment (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the size, in Bytes, of the Image memory space.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns The size of the Image memory space.
**/
UINT32
PeCoffGetSizeOfImage (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image preferred load address.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns The Image preferred load address.
**/
UINT64
PeCoffGetImageBase (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the size, in Bytes, of the Image Headers.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns The size of the Image Headers.
**/
UINT32
PeCoffGetSizeOfHeaders (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Returns whether the Image relocations have been stripped.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns Whether the Image relocations have been stripped.
**/
BOOLEAN
PeCoffGetRelocsStripped (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image load address PeCoffLoadImage() has loaded the Image to.
May be called only after PeCoffLoadImage() has succeeded.
@param[in,out] Context The context describing the Image. Must have been
initialised by PeCoffInitializeContext().
@returns The Image load address.
**/
UINTN
PeCoffLoaderGetImageAddress (
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieve the immediate data encoded in an ARM MOVW/MOVT instruciton pair.
@param[in] Instructions Pointer to an ARM MOVW/MOVT insturction pair.
@returns The Immediate address encoded in the instructions.
**/
UINT32
PeCoffThumbMovwMovtImmediateAddress (
IN CONST VOID *Instructions
);
/**
Relocate an ARM MOVW/MOVT immediate instruction instruction pair.
@param[in,out] Instructions Pointer to ARM MOVW/MOVT instruction pair.
@param[in] Adjust The delta to add to the addresses.
**/
VOID
PeCoffThumbMovwMovtImmediateFixup (
IN OUT VOID *Instructions,
IN UINT64 Adjust
);
#endif // PE_COFF_LIB2_H_

View File

@ -0,0 +1,122 @@
/** @file
Provides primitives to allocate and free memory buffers of various memory types and alignments.
The Phase Memory Allocation Library abstracts primitive memory allocation operations. This library
allows code to be written in a phase-independent manner because the allocation of memory in PEI, DXE,
and SMM (for example) is done via a different mechanism. Using a common library interface makes it
much easier to port algorithms from phase to phase.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef __PHASE_MEMORY_ALLOCATION_LIB_H__
#define __PHASE_MEMORY_ALLOCATION_LIB_H__
/**
Allocates one or more 4KB pages of a certain memory type.
Allocates the number of 4KB pages of a certain memory type and returns a pointer
to the allocated buffer. The buffer returned is aligned on a 4KB boundary.
@param Type The type of allocation to perform.
@param MemoryType The type of memory to allocate.
@param Pages The number of 4 KB pages to allocate.
@param Memory The pointer to a physical address. On input, the
way in which the address is used depends on the
value of Type.
@retval EFI_SUCCESS The requested pages were allocated.
@retval EFI_OUT_OF_RESOURCES The pages could not be allocated.
@retval EFI_NOT_FOUND The requested pages could not be found.
**/
EFI_STATUS
EFIAPI
PhaseAllocatePages (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN OUT EFI_PHYSICAL_ADDRESS *Memory
);
/**
Frees one or more 4KB pages that were previously allocated with one of the page allocation
functions in the Memory Allocation Library.
Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer.
Buffer must have been allocated on a previous call to the page allocation services
of the Memory Allocation Library. If it is not possible to free allocated pages,
then this function will perform no actions.
If Buffer was not allocated with a page allocation function in the Memory Allocation
Library, then ASSERT().
If Pages is zero, then ASSERT().
@param Memory The base physical address of the pages to be freed.
@param Pages The number of 4 KB pages to free.
@retval EFI_SUCCESS The requested pages were freed.
@retval EFI_NOT_FOUND The requested memory pages were not allocated with
PhaseAllocatePages().
**/
EFI_STATUS
EFIAPI
PhaseFreePages (
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN Pages
);
/**
Allocates a buffer of a certain pool type.
Allocates the number bytes specified by AllocationSize of a certain pool type and returns a
pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is
returned. If there is not enough memory remaining to satisfy the request, then NULL is returned.
@param MemoryType The type of memory to allocate.
@param AllocationSize The number of bytes to allocate.
@return A pointer to the allocated buffer or NULL if allocation fails.
**/
VOID *
EFIAPI
PhaseAllocatePool (
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN AllocationSize
);
/**
Frees a buffer that was previously allocated with one of the pool allocation functions in the
Memory Allocation Library.
Frees the buffer specified by Buffer. Buffer must have been allocated on a previous call to the
pool allocation services of the Memory Allocation Library. If it is not possible to free pool
resources, then this function will perform no actions.
If Buffer was not allocated with a pool allocation function in the Memory Allocation Library,
then ASSERT().
@param Buffer The pointer to the buffer to free.
**/
VOID
EFIAPI
PhaseFreePool (
IN VOID *Buffer
);
///
/// The memory type to allocate for calls to AllocatePages().
///
extern CONST EFI_MEMORY_TYPE gPhaseDefaultDataType;
///
/// The memory type to allocate for calls to AllocateCodePages().
///
extern CONST EFI_MEMORY_TYPE gPhaseDefaultCodeType;
#endif

View File

@ -0,0 +1,181 @@
/** @file
UEFI Image Loader library implementation for UE Images.
Copyright (c) 2021 - 2023, Marvin Häuser. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef UE_LIB_H_
#define UE_LIB_H_
#include <IndustryStandard/UeImage.h>
typedef struct {
CONST UINT8 *FileBuffer;
UINT32 UnsignedFileSize;
UINT8 Subsystem;
UINT8 Machine;
BOOLEAN FixedAddress;
BOOLEAN XIP;
UINT8 LastSegmentIndex;
UINT32 SegmentsFileOffset; // Unused for XIP
UINT32 SegmentAlignment;
CONST VOID *Segments;
UINT8 SegmentImageInfoIterSize;
BOOLEAN RelocsStripped;
UINT8 NumLoadTables;
UINT32 LoadTablesFileOffset;
UINT32 RelocTableSize;
CONST UE_LOAD_TABLE *LoadTables;
VOID *ImageBuffer;
UINT32 ImageSize;
UINT32 EntryPointAddress;
UINT64 BaseAddress; // Unused for XIP
} UE_LOADER_IMAGE_CONTEXT;
typedef struct UE_LOADER_RUNTIME_CONTEXT_ UE_LOADER_RUNTIME_CONTEXT;
/**
Adds the digest of Data to HashContext. This function can be called multiple
times to compute the digest of discontinuous data.
@param[in,out] HashContext The context of the current hash.
@param[in] Data The data to be hashed.
@param[in] DataSize The size, in Bytes, of Data.
@returns Whether hashing has been successful.
**/
typedef
BOOLEAN
(EFIAPI *UE_LOADER_HASH_UPDATE)(
IN OUT VOID *HashContext,
IN CONST VOID *Data,
IN UINTN DataSize
);
RETURN_STATUS
UeInitializeContextPreHash (
OUT UE_LOADER_IMAGE_CONTEXT *Context,
IN CONST VOID *FileBuffer,
IN UINT32 FileSize
);
RETURN_STATUS
UeInitializeContextPostHash (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context
);
BOOLEAN
UeHashImageDefault (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context,
IN OUT VOID *HashContext,
IN UE_LOADER_HASH_UPDATE HashUpdate
);
RETURN_STATUS
UeLoadImage (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context,
OUT VOID *Destination,
IN UINT32 DestinationSize
);
RETURN_STATUS
UeLoaderGetRuntimeContextSize (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context,
OUT UINT32 *Size
);
RETURN_STATUS
UeRelocateImage (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context,
IN UINT64 BaseAddress,
OUT UE_LOADER_RUNTIME_CONTEXT *RuntimeContext OPTIONAL,
IN UINT32 RuntimeContextSize
);
RETURN_STATUS
UeRelocateImageForRuntime (
IN OUT VOID *Image,
IN UINT32 ImageSize,
IN CONST UE_LOADER_RUNTIME_CONTEXT *RuntimeContext,
IN UINT64 BaseAddress
);
RETURN_STATUS
UeGetSymbolsPath (
IN CONST UE_LOADER_IMAGE_CONTEXT *Context,
OUT CONST CHAR8 **SymbolsPath,
OUT UINT32 *SymbolsPathSize
);
UINTN
UeLoaderGetImageDebugAddress (
IN CONST UE_LOADER_IMAGE_CONTEXT *Context
);
RETURN_STATUS
UeGetSegmentNames (
IN CONST UE_LOADER_IMAGE_CONTEXT *Context,
OUT CONST UE_SEGMENT_NAME **SegmentNames
);
UINT32
UeGetEntryPointAddress (
IN CONST UE_LOADER_IMAGE_CONTEXT *Context
);
UINT16
UeGetMachine (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context
);
UINT16
UeGetSubsystem (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context
);
UINT32
UeGetSegmentAlignment (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context
);
UINT32
UeGetImageSize (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context
);
UINT64
UeGetBaseAddress (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context
);
BOOLEAN
UeGetRelocsStripped (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context
);
BOOLEAN
UeGetFixedAddress(
IN OUT UE_LOADER_IMAGE_CONTEXT *Context
);
UINTN
UeLoaderGetImageAddress (
IN CONST UE_LOADER_IMAGE_CONTEXT *Context
);
UINT16
UeGetSegments (
IN CONST UE_LOADER_IMAGE_CONTEXT *Context,
OUT CONST UE_SEGMENT **Segments
);
UINT16
UeGetSegmentImageInfos (
IN OUT UE_LOADER_IMAGE_CONTEXT *Context,
OUT CONST UINT32 **SegmentImageInfos,
OUT UINT8 *SegmentImageInfoIterSize
);
#endif // UE_LIB_H_

View File

@ -0,0 +1,47 @@
/** @file
Provides services to perform additional actions when an UEFI image is loaded
or unloaded. This is useful for environment where symbols need to be loaded
and unloaded to support source level debugging.
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef __UEFI_IMAGE_EXTRA_ACTION_LIB_H__
#define __UEFI_IMAGE_EXTRA_ACTION_LIB_H__
#include <Library/UefiImageLib.h>
/**
Performs additional actions after a UEFI image has been loaded and relocated.
If ImageContext is NULL, then ASSERT().
@param ImageContext Pointer to the image context structure that describes the
UEFI image that has already been loaded and relocated.
**/
VOID
EFIAPI
UefiImageLoaderRelocateImageExtraAction (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *ImageContext
);
/**
Performs additional actions just before a UEFI image is unloaded. Any resources
that were allocated by UefiImageLoaderRelocateImageExtraAction() must be freed.
If ImageContext is NULL, then ASSERT().
@param ImageContext Pointer to the image context structure that describes the
UEFI image that is being unloaded.
**/
VOID
EFIAPI
UefiImageLoaderUnloadImageExtraAction (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *ImageContext
);
#endif

View File

@ -0,0 +1,722 @@
// FIXME: Docs
#ifndef UEFI_IMAGE_LIB_H_
#define UEFI_IMAGE_LIB_H_
#include <Library/UeImageLib.h>
#include <Library/PeCoffLib2.h>
typedef enum {
UefiImageFormatPe = 0,
UefiImageFormatUe = 1,
UefiImageFormatMax
} UEFI_IMAGE_FORMAT;
#define UEFI_IMAGE_SOURCE_NON_FV 0U
#define UEFI_IMAGE_SOURCE_FV 1U
#define UEFI_IMAGE_SOURCE_ALL 2U
#define UEFI_IMAGE_SOURCE_MAX 3U
// FIXME: Get rid of pointers.
typedef struct {
UINT32 ImageBuffer;
UINT32 AddressOfEntryPoint;
UINT8 ImageType;
UINT32 FileBuffer;
UINT32 ExeHdrOffset;
UINT32 SizeOfImage;
UINT32 FileSize;
UINT16 Subsystem;
UINT32 SectionAlignment;
UINT32 SectionsOffset;
UINT16 NumberOfSections;
UINT32 SizeOfHeaders;
} PE_HOB_IMAGE_CONTEXT;
typedef struct {
UINT32 ImageBuffer;
UINT32 FileBuffer;
UINT32 EntryPointAddress;
UINT32 LoadTablesFileOffset;
UINT8 NumLoadTables;
UINT32 LoadTables;
UINT32 Segments;
UINT8 LastSegmentIndex;
UINT32 SegmentAlignment;
UINT32 ImageSize;
UINT8 Subsystem;
UINT8 SegmentImageInfoIterSize;
UINT32 SegmentsFileOffset;
} UE_HOB_IMAGE_CONTEXT;
typedef struct {
UINT8 FormatIndex;
union {
UE_HOB_IMAGE_CONTEXT Ue;
PE_HOB_IMAGE_CONTEXT Pe;
} Ctx;
} HOB_IMAGE_CONTEXT;
typedef UINT8 UEFI_IMAGE_SOURCE;
typedef struct {
UINT8 FormatIndex;
union {
UE_LOADER_IMAGE_CONTEXT Ue;
PE_COFF_LOADER_IMAGE_CONTEXT Pe;
} Ctx;
} UEFI_IMAGE_LOADER_IMAGE_CONTEXT;
typedef struct UEFI_IMAGE_LOADER_RUNTIME_CONTEXT_ UEFI_IMAGE_LOADER_RUNTIME_CONTEXT;
///
/// Image record segment that desribes the UEFI memory permission configuration
/// for one segment of the Image.
///
typedef struct {
///
/// The size, in Bytes, of the Image record segment.
///
UINT32 Size;
///
/// The UEFI memory permission attributes corresponding to this Image record
/// segment.
///
UINT32 Attributes;
} UEFI_IMAGE_RECORD_SEGMENT;
///
/// The 32-bit signature that identifies a UEFI_IMAGE_RECORD structure.
///
#define UEFI_IMAGE_RECORD_SIGNATURE SIGNATURE_32 ('U','I','I','R')
///
/// Image record that describes the UEFI memory permission configuration for
/// every segment of the Image.
///
typedef struct {
///
/// The signature of the Image record structure. Must be set to
/// UEFI_IMAGE_RECORD_SIGNATURE.
///
UINT32 Signature;
///
/// The number of Image records. Must be at least 1.
///
UINT32 NumSegments;
///
/// A link to allow insertion of the Image record into a doubly-linked list.
///
LIST_ENTRY Link;
///
/// The start address of the Image memory space.
///
UINTN StartAddress;
///
/// The end address of the Image memory space. Must be equal to StartAddress
/// plus the sum of Segments[i].Size for 0 <= i < NumSegments.
///
UINTN EndAddress;
///
/// The Image record segments with their corresponding memory permission
/// attributes. All Image record segments are contiguous and cover the entire
/// Image memory space. The address of an Image record segment can be
/// determined by adding the sum of all previous sizes to StartAddress.
///
UEFI_IMAGE_RECORD_SEGMENT Segments[];
} UEFI_IMAGE_RECORD;
/**
Adds the digest of Data to HashContext. This function can be called multiple
times to compute the digest of discontinuous data.
@param[in,out] HashContext The context of the current hash.
@param[in] Data The data to be hashed.
@param[in] DataSize The size, in Bytes, of Data.
@returns Whether hashing has been successful.
**/
typedef
BOOLEAN
(EFIAPI *UEFI_IMAGE_LOADER_HASH_UPDATE)(
IN OUT VOID *HashContext,
IN CONST VOID *Data,
IN UINTN DataSize
);
// FIXME: Docs
RETURN_STATUS
UefiImageInitializeContextPreHash (
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN CONST VOID *FileBuffer,
IN UINT32 FileSize,
IN UEFI_IMAGE_SOURCE Source,
IN UINT8 ImageOrigin
);
RETURN_STATUS
UefiImageInitializeContextPostHash (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Verify the UEFI Image and initialise Context.
Used offsets and ranges must be aligned and in the bounds of the raw file.
Image headers and basic Relocation information must be well-formed.
FileBuffer must remain valid for the entire lifetime of Context.
@param[out] Context The context describing the Image.
@param[in] FileBuffer The file data to parse as UEFI Image.
@param[in] FileSize The size, in Bytes, of FileBuffer.
@param[in] Source Determines supported loaders (PE/UE).
@param[in] ImageOrigin Determines image protection policy.
@retval RETURN_SUCCESS The Image context has been initialised successfully.
@retval other The file data is malformed.
**/
RETURN_STATUS
UefiImageInitializeContext (
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN CONST VOID *FileBuffer,
IN UINT32 FileSize,
IN UEFI_IMAGE_SOURCE Source,
IN UINT8 ImageOrigin
);
/**
Retrieves the UefiImageLib Image format identifier.
@param[out] Context The context describing the Image.
@returns The UefiImageLib format identifier.
**/
UINT8
UefiImageGetFormat (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Hashes the Image using the format's default hashing algorithm.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@param[in,out] HashContext The context of the current hash. Must have been
initialised for usage with the HashUpdate
function.
@param[in] HashUpdate The data hashing function.
@returns Whether hashing has been successful.
**/
BOOLEAN
UefiImageHashImageDefault (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN OUT VOID *HashContext,
IN UEFI_IMAGE_LOADER_HASH_UPDATE HashUpdate
);
/**
Load the Image into the destination memory space.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@param[out] Destination The Image destination memory. Must be allocated
from page memory.
@param[in] DestinationSize The size, in Bytes, of Destination. Must be at
least as large as the size returned by
UefiImageGetImageSize().
@retval RETURN_SUCCESS The Image was loaded successfully.
@retval other The Image could not be loaded successfully.
**/
RETURN_STATUS
UefiImageLoadImage (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT VOID *Destination,
IN UINT32 DestinationSize
);
// FIXME: Docs
BOOLEAN
UefiImageImageIsInplace (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Equivalent to the UefiImageLoadImage() function for inplace-loading. Ensures that
all important raw file offsets match the respective RVAs.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@retval RETURN_SUCCESS The Image has been inplace-loaded successfully.
@retval other The Image is not suitable for inplace-loading.
**/
RETURN_STATUS
UefiImageLoadImageInplace (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
// FIXME: Docs
RETURN_STATUS
UefiImageRelocateImageInplace (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
// FIXME:
RETURN_STATUS
UefiImageRelocateImageInplaceForExecution (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the size required to bookkeep Image runtime relocation information.
May only be called when UefiImageGetRelocsStripped() returns FALSE.
@param[in,out] Context The context describing the Image. Must have been
loaded by UefiImageLoadImage().
@param[out] Size On output, the size, in Bytes, required for the
bookkeeping buffer.
@retval RETURN_SUCCESS The Image runtime context size for the Image was
retrieved successfully.
@retval other The Image runtime context size for the Image could not
be retrieved successfully.
**/
RETURN_STATUS
UefiImageLoaderGetRuntimeContextSize (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT UINT32 *Size
);
/**
Relocate the Image for boot-time usage.
May only be called when UefiImageGetRelocsStripped() returns FALSE, or with
BaseAddress == UefiImageGetBaseAddress().
@param[in,out] Context The context describing the Image. Must have
been loaded by UefiImageLoadImage().
@param[in] BaseAddress The address to relocate the Image to.
@param[out] RuntimeContext If not NULL, on output, a bookkeeping data
required for Image runtime relocation.
@param[in] RuntimeContextSize The size, in Bytes, of RuntimeContext. Must
be at least as big as the size returned by
UefiImageLoaderGetRuntimeContextSize().
@retval RETURN_SUCCESS The Image has been relocated successfully.
@retval other The Image Relocation Directory is malformed.
**/
RETURN_STATUS
UefiImageRelocateImage (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN UINT64 BaseAddress,
OUT UEFI_IMAGE_LOADER_RUNTIME_CONTEXT *RuntimeContext OPTIONAL,
IN UINT32 RuntimeContextSize
);
/**
Load the Image into the destination memory space, relocate Image for boot-time
usage, and perform environment-specific actions required to execute code from
the Image.
May only be called when UefiImageGetRelocsStripped() returns FALSE, or with
BaseAddress == UefiImageGetBaseAddress().
@param[in,out] Context The context describing the Image. Must have
been initialised by
UefiImageInitializeContext().
@param[out] Destination The Image destination memory. Must be
allocated from page memory.
@param[in] DestinationSize The size, in Bytes, of Destination. Must be
at least as large as the size returned by
UefiImageGetImageSize().
@param[out] RuntimeContext If not NULL, on output, a buffer
bookkeeping data required for Image runtime
relocation.
@param[in] RuntimeContextSize The size, in Bytes, of RuntimeContext. Must
be at least as big as the size returned by
UefiImageLoaderGetRuntimeContextSize().
@retval RETURN_SUCCESS The Image was loaded successfully.
@retval other The Image could not be loaded successfully.
**/
RETURN_STATUS
UefiImageLoadImageForExecution (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT VOID *Destination,
IN UINT32 DestinationSize,
OUT UEFI_IMAGE_LOADER_RUNTIME_CONTEXT *RuntimeContext OPTIONAL,
IN UINT32 RuntimeContextSize
);
/**
Relocate Image for Runtime usage.
May only be called when UefiImageGetRelocsStripped() returns FALSE, or with
BaseAddress == UefiImageGetBaseAddress().
@param[in,out] Image The Image destination memory. Must have been
relocated by UefiImageRelocateImage().
@param[in] ImageSize The size, in Bytes, of Image.
@param[in] BaseAddress The address to relocate the Image to.
@param[in] RuntimeContext The Relocation context obtained by
UefiImageRelocateImage().
@retval RETURN_SUCCESS The Image has been relocated successfully.
@retval other The Image could not be relocated successfully.
**/
RETURN_STATUS
UefiImageRuntimeRelocateImage (
IN OUT VOID *Image,
IN UINT32 ImageSize,
IN UINT64 BaseAddress,
IN CONST UEFI_IMAGE_LOADER_RUNTIME_CONTEXT *RuntimeContext
);
/**
Relocate Image for Runtime usage, and perform environment-specific actions
required to execute code from the Image.
May only be called when UefiImageGetRelocsStripped() returns FALSE, or with
BaseAddress == UefiImageGetBaseAddress().
@param[in,out] Image The Image destination memory. Must have been
relocated by UefiImageRelocateImage().
@param[in] ImageSize The size, in Bytes, of Image.
@param[in] BaseAddress The address to relocate the Image to.
@param[in] RuntimeContext The Relocation context obtained by
UefiImageRelocateImage().
@retval RETURN_SUCCESS The Image has been relocated successfully.
@retval other The Image could not be relocated successfully.
**/
RETURN_STATUS
UefiImageRuntimeRelocateImageForExecution (
IN OUT VOID *Image,
IN UINT32 ImageSize,
IN UINT64 BaseAddress,
IN CONST UEFI_IMAGE_LOADER_RUNTIME_CONTEXT *RuntimeContext
);
/**
Discards optional Image segments to disguise sensitive data.
This may destruct the Image Relocation Directory and as such, no function that
performs Image relocation may be called after this function has been invoked.
@param[in,out] Context The context describing the Image. Must have been
loaded by UefiImageLoadImage().
**/
VOID
UefiImageDiscardSegments (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image symbols path.
@param[in,out] Context The context describing the Image. Must have
been initialised by
UefiImageInitializeContext().
@param[out] SymbolsPath On output, a pointer to the Image symbols
path.
@param[out] SymbolsPathSize On output, the size, in Bytes, of *
SymbolsPath.
@retval RETURN_SUCCESS The Image symbols path was retrieved successfully.
@retval other The Image symbols path could not be retrieved
successfully.
**/
RETURN_STATUS
UefiImageGetSymbolsPath (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT CONST CHAR8 **SymbolsPath,
OUT UINT32 *SymbolsPathSize
);
/**
Retrieves the Image module name from the Image symbols path.
@param[in,out] Context The context describing the Image. Must have
been initialised by
UefiImageInitializeContext().
@param[out] ModuleName Buffer the Image module name is written into.
If the name exceeds ModuleNameSize, it will be
truncated.
@param[out] ModuleNameSize The size, in Bytes, of ModuleName.
@retval RETURN_SUCCESS The Image module name was retrieved successfully.
@retval other The Image module name could not be retrieved
successfully.
**/
RETURN_STATUS
UefiImageGetModuleNameFromSymbolsPath (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT CHAR8 *ModuleName,
IN UINT32 ModuleNameSize
);
// FIXME: Abstract certificates somehow?
/**
Retrieves the first certificate from the Image Certificate Directory.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@param[out] Certificate On output, the first certificate of the Image.
@retval RETURN_SUCCESS The certificate has been retrieved successfully.
@retval RETURN_NOT_FOUND There is no such certificate.
@retval other The Image Certificate Directory is malformed.
**/
RETURN_STATUS
UefiImageGetFirstCertificate (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT CONST WIN_CERTIFICATE **Certificate
);
/**
Retrieves the next certificate from the Image Certificate Directory.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@param[out] Certificate On input, the current certificate of the Image.
Must have been retrieved by
UefiImageGetFirstCertificate().
On output, the next certificate of the Image.
@retval RETURN_SUCCESS The certificate has been retrieved successfully.
@retval RETURN_NOT_FOUND There is no such certificate.
@retval other The Image Certificate Directory is malformed.
**/
RETURN_STATUS
UefiImageGetNextCertificate (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN OUT CONST WIN_CERTIFICATE **Certificate
);
/**
Retrieves the Image HII data RVA.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@param[out] HiiRva On output, the RVA of the HII resource data.
@param[out] HiiSize On output, the size, in Bytes, of HiiRva.
@retval RETURN_SUCCESS The Image HII data has been retrieved successfully.
@retval RETURN_NOT_FOUND The Image HII data could not be found.
@retval other The Image Resource Directory is malformed.
**/
RETURN_STATUS
UefiImageGetHiiDataRva (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT UINT32 *HiiRva,
OUT UINT32 *HiiSize
);
/**
Retrieve the Image entry point RVA.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The Image entry point RVA.
**/
UINT32
UefiImageGetEntryPointAddress (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image machine type.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The Image machine type.
**/
UINT16
UefiImageGetMachine (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image subsystem type.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The Image subsystem type.
**/
UINT16
UefiImageGetSubsystem (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image segment alignment.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The Image segment alignment.
**/
UINT32
UefiImageGetSegmentAlignment (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the size, in Bytes, of the Image memory space.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The size of the Image memory space.
**/
UINT32
UefiImageGetImageSize (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image preferred load address.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The Image preferred load address.
**/
UINT64
UefiImageGetBaseAddress (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Returns whether the Image relocations have been stripped.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns Whether the Image relocations have been stripped.
**/
BOOLEAN
UefiImageGetRelocsStripped (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image load address UefiImageLoadImage() has loaded the Image to.
May be called only after UefiImageLoadImage() has succeeded.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The Image load address.
**/
UINTN
UefiImageLoaderGetImageAddress (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieves the Image debug address. Due to post-processing, the debug address
may deviate from the load address. Symbolication must use this address.
May be called only after UefiImageLoadImage() has succeeded.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The Image debug address.
**/
UINTN
UefiImageLoaderGetDebugAddress (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Retrieve the Image entry point address.
May be called only after UefiImageLoadImage() has succeeded.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@returns The Image entry point addres.
**/
UINTN
UefiImageLoaderGetImageEntryPoint (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
/**
Constructs an Image record from the Image. Any headers, gaps, or trailers are
described as read-only data.
May be called only after UefiImageLoadImage() has succeeded.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@retval NULL The Image record could not constructed successfully.
@retval other The Image record was constructed successfully and is returned.
It is allocated using the AllocatePool() API and is
caller-owned as soon as this function returns.
**/
UEFI_IMAGE_RECORD *
UefiImageLoaderGetImageRecord (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
// FIXME: Docs
RETURN_STATUS
UefiImageDebugLocateImage (
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN UINTN Address,
IN UINT8 ImageOrigin
);
/**
Retrieve the Image fixed loading address.
This function is only guaranteed to function correctly if the Image was built
by a tool with this feature enabled.
@param[in,out] Context The context describing the Image. Must have been
initialised by UefiImageInitializeContext().
@param[out] Address On output, the fixed loading address of the Image.
*Address is guaranteed to by aligned by the Image
segment alignment, and thus the size returned by
UefiImageGetImageSize is sufficient to hold the
Image.
@retval RETURN_SUCCESS The Image has a fixed loading address.
@retval RETURN_NOT_FOUND The Image does not have a fixed loading address.
@retval RETURN_UNSUPPORTED The Image fixed loading address is unaligned.
**/
RETURN_STATUS
UefiImageGetFixedAddress (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT UINT64 *Address
);
// FIXME: Docs
VOID
UefiImageDebugPrintSegments (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
VOID
UefiImageDebugPrintImageRecord (
IN CONST UEFI_IMAGE_RECORD *ImageRecord
);
UINTN
UefiImageLoaderGetDebugAddress (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
);
#endif // UEFI_IMAGE_LIB_H_

View File

@ -7,6 +7,7 @@
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
Portions copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.<BR>
Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
@ -15,8 +16,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#ifndef __DEBUG_SUPPORT_H__
#define __DEBUG_SUPPORT_H__
#include <IndustryStandard/PeImage.h>
typedef struct _EFI_DEBUG_SUPPORT_PROTOCOL EFI_DEBUG_SUPPORT_PROTOCOL;
///
@ -428,7 +427,6 @@ typedef struct {
// virtual registers - nat bits for R1-R31
//
UINT64 IntNat;
} EFI_SYSTEM_CONTEXT_IPF;
///
@ -467,8 +465,6 @@ typedef struct {
UINT64 Ip;
} EFI_SYSTEM_CONTEXT_EBC;
///
/// ARM processor exception types.
///
@ -513,7 +509,6 @@ typedef struct {
UINT32 IFAR;
} EFI_SYSTEM_CONTEXT_ARM;
///
/// AARCH64 processor exception types.
///
@ -603,6 +598,177 @@ typedef struct {
UINT64 FAR; // Fault Address Register
} EFI_SYSTEM_CONTEXT_AARCH64;
///
/// RISC-V processor exception types.
///
#define EXCEPT_RISCV_INST_MISALIGNED 0
#define EXCEPT_RISCV_INST_ACCESS_FAULT 1
#define EXCEPT_RISCV_ILLEGAL_INST 2
#define EXCEPT_RISCV_BREAKPOINT 3
#define EXCEPT_RISCV_LOAD_ADDRESS_MISALIGNED 4
#define EXCEPT_RISCV_LOAD_ACCESS_FAULT 5
#define EXCEPT_RISCV_STORE_AMO_ADDRESS_MISALIGNED 6
#define EXCEPT_RISCV_STORE_AMO_ACCESS_FAULT 7
#define EXCEPT_RISCV_ENV_CALL_FROM_UMODE 8
#define EXCEPT_RISCV_ENV_CALL_FROM_SMODE 9
#define EXCEPT_RISCV_ENV_CALL_FROM_VS_MODE 10
#define EXCEPT_RISCV_ENV_CALL_FROM_MMODE 11
#define EXCEPT_RISCV_INST_ACCESS_PAGE_FAULT 12
#define EXCEPT_RISCV_LOAD_ACCESS_PAGE_FAULT 13
#define EXCEPT_RISCV_14 14
#define EXCEPT_RISCV_STORE_ACCESS_PAGE_FAULT 15
#define EXCEPT_RISCV_16 16
#define EXCEPT_RISCV_17 17
#define EXCEPT_RISCV_18 18
#define EXCEPT_RISCV_19 19
#define EXCEPT_RISCV_INST_GUEST_PAGE_FAULT 20
#define EXCEPT_RISCV_LOAD_GUEST_PAGE_FAULT 21
#define EXCEPT_RISCV_VIRTUAL_INSTRUCTION 22
#define EXCEPT_RISCV_STORE_GUEST_PAGE_FAULT 23
#define EXCEPT_RISCV_MAX_EXCEPTIONS (EXCEPT_RISCV_STORE_GUEST_PAGE_FAULT)
///
/// RISC-V processor exception types for interrupts.
///
#define EXCEPT_RISCV_IS_IRQ(x) ((x & 0x8000000000000000UL) != 0)
#define EXCEPT_RISCV_IRQ_INDEX(x) (x & 0x7FFFFFFFFFFFFFFFUL)
#define EXCEPT_RISCV_IRQ_0 0x8000000000000000UL
#define EXCEPT_RISCV_IRQ_SOFT_FROM_SMODE 0x8000000000000001UL
#define EXCEPT_RISCV_IRQ_SOFT_FROM_VSMODE 0x8000000000000002UL
#define EXCEPT_RISCV_IRQ_SOFT_FROM_MMODE 0x8000000000000003UL
#define EXCEPT_RISCV_IRQ_4 0x8000000000000004UL
#define EXCEPT_RISCV_IRQ_TIMER_FROM_SMODE 0x8000000000000005UL
#define EXCEPT_RISCV_MAX_IRQS (EXCEPT_RISCV_IRQ_INDEX(EXCEPT_RISCV_IRQ_TIMER_FROM_SMODE))
typedef struct {
UINT64 X0;
UINT64 X1;
UINT64 X2;
UINT64 X3;
UINT64 X4;
UINT64 X5;
UINT64 X6;
UINT64 X7;
UINT64 X8;
UINT64 X9;
UINT64 X10;
UINT64 X11;
UINT64 X12;
UINT64 X13;
UINT64 X14;
UINT64 X15;
UINT64 X16;
UINT64 X17;
UINT64 X18;
UINT64 X19;
UINT64 X20;
UINT64 X21;
UINT64 X22;
UINT64 X23;
UINT64 X24;
UINT64 X25;
UINT64 X26;
UINT64 X27;
UINT64 X28;
UINT64 X29;
UINT64 X30;
UINT64 X31;
UINT64 SEPC;
UINT32 SSTATUS;
UINT32 STVAL;
} EFI_SYSTEM_CONTEXT_RISCV64;
//
// LoongArch processor exception types.
//
#define EXCEPT_LOONGARCH_INT 0
#define EXCEPT_LOONGARCH_PIL 1
#define EXCEPT_LOONGARCH_PIS 2
#define EXCEPT_LOONGARCH_PIF 3
#define EXCEPT_LOONGARCH_PME 4
#define EXCEPT_LOONGARCH_PNR 5
#define EXCEPT_LOONGARCH_PNX 6
#define EXCEPT_LOONGARCH_PPI 7
#define EXCEPT_LOONGARCH_ADE 8
#define EXCEPT_LOONGARCH_ALE 9
#define EXCEPT_LOONGARCH_BCE 10
#define EXCEPT_LOONGARCH_SYS 11
#define EXCEPT_LOONGARCH_BRK 12
#define EXCEPT_LOONGARCH_INE 13
#define EXCEPT_LOONGARCH_IPE 14
#define EXCEPT_LOONGARCH_FPD 15
#define EXCEPT_LOONGARCH_SXD 16
#define EXCEPT_LOONGARCH_ASXD 17
#define EXCEPT_LOONGARCH_FPE 18
#define EXCEPT_LOONGARCH_TBR 64 // For code only, there is no such type in the ISA spec, the TLB refill is defined for an independent exception.
//
// LoongArch processor Interrupt types.
//
#define EXCEPT_LOONGARCH_INT_SIP0 0
#define EXCEPT_LOONGARCH_INT_SIP1 1
#define EXCEPT_LOONGARCH_INT_IP0 2
#define EXCEPT_LOONGARCH_INT_IP1 3
#define EXCEPT_LOONGARCH_INT_IP2 4
#define EXCEPT_LOONGARCH_INT_IP3 5
#define EXCEPT_LOONGARCH_INT_IP4 6
#define EXCEPT_LOONGARCH_INT_IP5 7
#define EXCEPT_LOONGARCH_INT_IP6 8
#define EXCEPT_LOONGARCH_INT_IP7 9
#define EXCEPT_LOONGARCH_INT_PMC 10
#define EXCEPT_LOONGARCH_INT_TIMER 11
#define EXCEPT_LOONGARCH_INT_IPI 12
//
// For coding convenience, define the maximum valid
// LoongArch interrupt.
//
#define MAX_LOONGARCH_INTERRUPT 14
typedef struct {
UINT64 R0;
UINT64 R1;
UINT64 R2;
UINT64 R3;
UINT64 R4;
UINT64 R5;
UINT64 R6;
UINT64 R7;
UINT64 R8;
UINT64 R9;
UINT64 R10;
UINT64 R11;
UINT64 R12;
UINT64 R13;
UINT64 R14;
UINT64 R15;
UINT64 R16;
UINT64 R17;
UINT64 R18;
UINT64 R19;
UINT64 R20;
UINT64 R21;
UINT64 R22;
UINT64 R23;
UINT64 R24;
UINT64 R25;
UINT64 R26;
UINT64 R27;
UINT64 R28;
UINT64 R29;
UINT64 R30;
UINT64 R31;
UINT64 CRMD; // CuRrent MoDe information
UINT64 PRMD; // PRe-exception MoDe information
UINT64 EUEN; // Extended component Unit ENable
UINT64 MISC; // MISCellaneous controller
UINT64 ECFG; // Exception ConFiGuration
UINT64 ESTAT; // Exception STATus
UINT64 ERA; // Exception Return Address
UINT64 BADV; // BAD Virtual address
UINT64 BADI; // BAD Instruction
} EFI_SYSTEM_CONTEXT_LOONGARCH64;
///
/// Universal EFI_SYSTEM_CONTEXT definition.
@ -614,6 +780,8 @@ typedef union {
EFI_SYSTEM_CONTEXT_IPF *SystemContextIpf;
EFI_SYSTEM_CONTEXT_ARM *SystemContextArm;
EFI_SYSTEM_CONTEXT_AARCH64 *SystemContextAArch64;
EFI_SYSTEM_CONTEXT_RISCV64 *SystemContextRiscV64;
EFI_SYSTEM_CONTEXT_LOONGARCH64 *SystemContextLoongArch64;
} EFI_SYSTEM_CONTEXT;
//
@ -650,15 +818,14 @@ VOID
/// Machine type definition
///
typedef enum {
IsaIa32 = IMAGE_FILE_MACHINE_I386, ///< 0x014C
IsaX64 = IMAGE_FILE_MACHINE_X64, ///< 0x8664
IsaIpf = IMAGE_FILE_MACHINE_IA64, ///< 0x0200
IsaEbc = IMAGE_FILE_MACHINE_EBC, ///< 0x0EBC
IsaArm = IMAGE_FILE_MACHINE_ARMTHUMB_MIXED, ///< 0x01c2
IsaAArch64 = IMAGE_FILE_MACHINE_ARM64 ///< 0xAA64
IsaIa32 = EFI_IMAGE_MACHINE_IA32, ///< 0x014C
IsaX64 = EFI_IMAGE_MACHINE_X64, ///< 0x8664
IsaIpf = EFI_IMAGE_MACHINE_IA64, ///< 0x0200
IsaEbc = EFI_IMAGE_MACHINE_EBC, ///< 0x0EBC
IsaArm = EFI_IMAGE_MACHINE_ARMTHUMB_MIXED, ///< 0x01c2
IsaAArch64 = EFI_IMAGE_MACHINE_AARCH64 ///< 0xAA64
} EFI_INSTRUCTION_SET_ARCHITECTURE;
//
// DebugSupport member function definitions
//

View File

@ -41,10 +41,6 @@ typedef struct _EFI_TLS_PROTOCOL EFI_TLS_PROTOCOL;
/// EFI_TLS_SESSION_DATA_TYPE
///
typedef enum {
///
/// Session Configuration
///
///
/// TLS session Version. The corresponding Data is of type EFI_TLS_VERSION.
///
@ -86,11 +82,6 @@ typedef enum {
/// The corresponding Data is of type EFI_TLS_SESSION_STATE.
///
EfiTlsSessionState,
///
/// Session information
///
///
/// TLS session data client random.
/// The corresponding Data is of type EFI_TLS_RANDOM.
@ -106,9 +97,15 @@ typedef enum {
/// The corresponding Data is of type EFI_TLS_MASTER_SECRET.
///
EfiTlsKeyMaterial,
///
/// TLS session hostname for validation which is used to verify whether the name
/// within the peer certificate matches a given host name.
/// This parameter is invalid when EfiTlsVerifyMethod is EFI_TLS_VERIFY_NONE.
/// The corresponding Data is of type EFI_TLS_VERIFY_HOST.
///
EfiTlsVerifyHost,
EfiTlsSessionDataTypeMaximum
} EFI_TLS_SESSION_DATA_TYPE;
///
@ -187,6 +184,54 @@ typedef UINT32 EFI_TLS_VERIFY;
///
#define EFI_TLS_VERIFY_CLIENT_ONCE 0x4
///
/// EFI_TLS_VERIFY_HOST_FLAG
///
typedef UINT32 EFI_TLS_VERIFY_HOST_FLAG;
///
/// There is no additional flags set for hostname validation.
/// Wildcards are supported and they match only in the left-most label.
///
#define EFI_TLS_VERIFY_FLAG_NONE 0x00
///
/// Always check the Subject Distinguished Name (DN) in the peer certificate even if the
/// certificate contains Subject Alternative Name (SAN).
///
#define EFI_TLS_VERIFY_FLAG_ALWAYS_CHECK_SUBJECT 0x01
///
/// Disable the match of all wildcards.
///
#define EFI_TLS_VERIFY_FLAG_NO_WILDCARDS 0x02
///
/// Disable the "*" as wildcard in labels that have a prefix or suffix (e.g. "www*" or "*www").
///
#define EFI_TLS_VERIFY_FLAG_NO_PARTIAL_WILDCARDS 0x04
///
/// Allow the "*" to match more than one labels. Otherwise, only matches a single label.
///
#define EFI_TLS_VERIFY_FLAG_MULTI_LABEL_WILDCARDS 0x08
///
/// Restrict to only match direct child sub-domains which start with ".".
/// For example, a name of ".example.com" would match "www.example.com" with this flag,
/// but would not match "www.sub.example.com".
///
#define EFI_TLS_VERIFY_FLAG_SINGLE_LABEL_SUBDOMAINS 0x10
///
/// Never check the Subject Distinguished Name (DN) even there is no
/// Subject Alternative Name (SAN) in the certificate.
///
#define EFI_TLS_VERIFY_FLAG_NEVER_CHECK_SUBJECT 0x20
///
/// EFI_TLS_VERIFY_HOST
///
#pragma pack (1)
typedef struct {
EFI_TLS_VERIFY_HOST_FLAG Flags;
CHAR8 *HostName;
} EFI_TLS_VERIFY_HOST;
#pragma pack ()
///
/// EFI_TLS_RANDOM
/// Note: The definition of EFI_TLS_RANDOM is from "RFC 5246 A.4.1.

View File

@ -322,9 +322,9 @@ typedef union {
///
UINT32 RDRAND:1;
///
/// [Bit 31] Always returns 0.
/// [Bit 31] A value of 1 indicates that processor is in Para-Virtualized.
///
UINT32 NotUsed:1;
UINT32 ParaVirtualized : 1;
} Bits;
///
/// All bit fields as a 32-bit value

View File

@ -624,3 +624,146 @@ CalculateCrc32(
return Crc ^ 0xffffffff;
}
GLOBAL_REMOVE_IF_UNREFERENCED STATIC CONST UINT16 mCrc16LookupTable[256] =
{
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
/**
Calculates the CRC16-ANSI checksum of the given buffer.
@param[in] Buffer Pointer to the buffer.
@param[in] Length Length of the buffer, in bytes.
@param[in] InitialValue Initial value of the CRC.
@return The CRC16-ANSI checksum.
**/
UINT16
EFIAPI
CalculateCrc16Ansi (
IN CONST VOID *Buffer,
IN UINTN Length,
IN UINT16 InitialValue
)
{
CONST UINT8 *Buf;
UINT16 Crc;
Buf = Buffer;
Crc = InitialValue;
while (Length-- != 0) {
Crc = mCrc16LookupTable[(Crc & 0xFF) ^ *(Buf++)] ^ (Crc >> 8);
}
return Crc;
}
GLOBAL_REMOVE_IF_UNREFERENCED STATIC CONST UINT32 mCrc32cLookupTable[256] = {
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c,
0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c,
0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc,
0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512,
0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad,
0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf,
0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f,
0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f,
0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e,
0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e,
0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de,
0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4,
0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b,
0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5,
0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975,
0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905,
0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8,
0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8,
0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78,
0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6,
0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69,
0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351
};
/**
Calculates the CRC32c checksum of the given buffer.
@param[in] Buffer Pointer to the buffer.
@param[in] Length Length of the buffer, in bytes.
@param[in] InitialValue Initial value of the CRC.
@return The CRC32c checksum.
**/
UINT32
EFIAPI
CalculateCrc32c (
IN CONST VOID *Buffer,
IN UINTN Length,
IN UINT32 InitialValue
)
{
CONST UINT8 *Buf;
UINT32 Crc;
Buf = Buffer;
Crc = ~InitialValue;
while (Length-- != 0) {
Crc = mCrc32cLookupTable[(Crc & 0xFF) ^ *(Buf++)] ^ (Crc >> 8);
}
return ~Crc;
}

View File

@ -0,0 +1,36 @@
/** @file
BaseOverflowLib
Copyright (c) 2020, Download-Fritz
All rights reserved.
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 <Base.h>
#include <Library/BaseLib.h>
#include <Library/BaseOverflowLib.h>
BOOLEAN
BaseOverflowAlignUpU32 (
UINT32 Value,
UINT32 Alignment,
UINT32 *Result
)
{
BOOLEAN Status;
Status = BaseOverflowAddU32 (Value, Alignment - 1U, Result);
*Result &= ~(Alignment - 1U);
return Status;
}

View File

@ -0,0 +1,442 @@
/** @file
BaseOverflowLib
Copyright (c) 2018, vit9696
All rights reserved.
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 <Base.h>
#include <Library/BaseLib.h>
#include <Library/BaseOverflowLib.h>
#include "BaseOverflowInternals.h"
//
// Software implementations provided try not to be obviously slow, but primarily
// target C99 compliance rather than performance.
//
BOOLEAN
BaseOverflowAddU16 (
UINT16 A,
UINT16 B,
UINT16 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_add_overflow (A, B, Result);
#else
UINT32 Temp;
//
// I believe casting will be faster on X86 at least.
//
Temp = A + B;
*Result = (UINT16)Temp;
if (Temp <= MAX_UINT16) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowSubU16 (
UINT16 A,
UINT16 B,
UINT16 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_sub_overflow (A, B, Result);
#else
*Result = (UINT16)(A - B);
if (A >= B) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowMulU16 (
UINT16 A,
UINT16 B,
UINT16 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_mul_overflow (A, B, Result);
#else
UINT32 Temp;
Temp = (UINT32)A * B;
*Result = (UINT16)Temp;
if (Temp <= MAX_UINT32) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowAddU32 (
UINT32 A,
UINT32 B,
UINT32 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_add_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS_64)
return __builtin_uadd_overflow (A, B, Result);
#else
UINT32 Temp;
//
// I believe casting will be faster on X86 at least.
//
Temp = A + B;
*Result = Temp;
if (Temp >= A) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowSubU32 (
UINT32 A,
UINT32 B,
UINT32 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_sub_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_usub_overflow (A, B, Result);
#else
*Result = A - B;
if (B <= A) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowMulU32 (
UINT32 A,
UINT32 B,
UINT32 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_mul_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_umul_overflow (A, B, Result);
#else
UINT64 Temp;
Temp = (UINT64)A * B;
*Result = (UINT32)Temp;
if (Temp <= MAX_UINT32) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowAddS32 (
INT32 A,
INT32 B,
INT32 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_add_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_sadd_overflow (A, B, Result);
#else
INT64 Temp;
Temp = (INT64)A + B;
*Result = (INT32)Temp;
if ((Temp >= MIN_INT32) && (Temp <= MAX_INT32)) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowSubS32 (
INT32 A,
INT32 B,
INT32 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_sub_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_ssub_overflow (A, B, Result);
#else
INT64 Temp;
Temp = (INT64)A - B;
*Result = (INT32)Temp;
if ((Temp >= MIN_INT32) && (Temp <= MAX_INT32)) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowMulS32 (
INT32 A,
INT32 B,
INT32 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_mul_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_smul_overflow (A, B, Result);
#else
INT64 Temp;
Temp = MultS64x64 (A, B);
*Result = (INT32)Temp;
if ((Temp >= MIN_INT32) && (Temp <= MAX_INT32)) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowAddU64 (
UINT64 A,
UINT64 B,
UINT64 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_add_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_uaddll_overflow (A, B, Result);
#else
UINT64 Temp;
Temp = A + B;
*Result = Temp;
if (Temp >= A) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowSubU64 (
UINT64 A,
UINT64 B,
UINT64 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_sub_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_usubll_overflow (A, B, Result);
#else
*Result = A - B;
if (B <= A) {
return FALSE;
}
return TRUE;
#endif
}
BOOLEAN
BaseOverflowMulU64 (
UINT64 A,
UINT64 B,
UINT64 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_mul_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_umulll_overflow (A, B, Result);
#else
UINT64 AHi;
UINT64 ALo;
UINT64 BHi;
UINT64 BLo;
UINT64 LoBits;
UINT64 HiBits1;
UINT64 HiBits2;
BOOLEAN Overflow;
//
// Based on the 2nd option written by Charphacy, believed to be the fastest portable on x86.
// See: https://stackoverflow.com/a/26320664
// Implements overflow checking by a series of up to 3 multiplications.
//
AHi = RShiftU64 (A, 32);
ALo = A & MAX_UINT32;
BHi = RShiftU64 (B, 32);
BLo = B & MAX_UINT32;
LoBits = MultU64x64 (ALo, BLo);
if ((AHi == 0) && (BHi == 0)) {
*Result = LoBits;
return FALSE;
}
Overflow = AHi > 0 && BHi > 0;
HiBits1 = MultU64x64 (ALo, BHi);
HiBits2 = MultU64x64 (AHi, BLo);
*Result = LoBits + LShiftU64 (HiBits1 + HiBits2, 32);
return Overflow || *Result < LoBits || RShiftU64 (HiBits1, 32) != 0 || RShiftU64 (HiBits2, 32) != 0;
#endif
}
BOOLEAN
BaseOverflowAddS64 (
INT64 A,
INT64 B,
INT64 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_add_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_saddll_overflow (A, B, Result);
#else
if (((B <= 0) || (A <= MAX_INT64 - B)) && ((B >= 0) || (A >= MIN_INT64 - B))) {
*Result = A + B;
return FALSE;
}
//
// Assign some defined value to *Result.
//
*Result = 0;
return TRUE;
#endif
}
BOOLEAN
BaseOverflowSubS64 (
INT64 A,
INT64 B,
INT64 *Result
)
{
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS)
return __builtin_sub_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS)
return __builtin_ssubll_overflow (A, B, Result);
#else
if (((B >= 0) || (A <= MAX_INT64 + B)) && ((B <= 0) || (A >= MIN_INT64 + B))) {
*Result = A - B;
return FALSE;
}
//
// Assign some defined value to *Result.
//
*Result = 0;
return TRUE;
#endif
}
BOOLEAN
BaseOverflowMulS64 (
INT64 A,
INT64 B,
INT64 *Result
)
{
//
// Intel 32-bit architectures do not have hardware signed 64-bit
// multiplication with overflow.
//
#if defined (BASE_HAS_TYPE_GENERIC_BUILTINS) && !defined (MDE_CPU_IA32)
return __builtin_mul_overflow (A, B, Result);
#elif defined (BASE_HAS_TYPE_SPECIFIC_BUILTINS) && !defined (MDE_CPU_IA32)
return __builtin_smulll_overflow (A, B, Result);
#else
UINT64 AU;
UINT64 BU;
UINT64 ResultU;
//
// It hurts to implement it without unsigned multiplication, maybe rewrite it one day.
// The idea taken from BaseSafeIntLib.
//
#define OC_ABS_64(X) (((X) < 0) ? (((UINT64) (-((X) + 1))) + 1) : (UINT64) (X))
AU = OC_ABS_64 (A);
BU = OC_ABS_64 (B);
if (BaseOverflowMulU64 (AU, BU, &ResultU)) {
*Result = 0;
return TRUE;
}
//
// Split into positive and negative results and check just one range.
//
if ((A < 0) == (B < 0)) {
if (ResultU <= MAX_INT64) {
*Result = (INT64)ResultU;
return FALSE;
}
} else {
if (ResultU < OC_ABS_64 (MIN_INT64)) {
*Result = -((INT64)ResultU);
return FALSE;
} else if (ResultU == OC_ABS_64 (MIN_INT64)) {
*Result = MIN_INT64;
return FALSE;
}
}
#undef OC_ABS_64
*Result = 0;
return TRUE;
#endif
}

Some files were not shown because too many files have changed in this diff Show More