mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
MdeModulePkg/GptLib: Add host-based unit tests for valid GPT behavior
Add the positive-path host-based tests for the shared GptLib parser (extracted as part of the parser security hardening), ensuring the tightened checks in PartitionValidGptTable(), PartitionCheckGptEntry() and PartitionRestoreGptTable() do not falsely reject well-formed GPTs. The tests run against an in-memory mock disk and cover accepted primary/backup headers, boundary but legal header/entry sizes, correct entry-status flagging on valid entries, and primary/backup restore round-trips. Signed-off-by: Richard Lyu <richard.lyu@suse.com>
This commit is contained in:
parent
b65fd21591
commit
77585e5004
6 changed files with 931 additions and 0 deletions
477
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTest.c
Normal file
477
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTest.c
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
/** @file
|
||||
Host-based unit tests for GptLib.
|
||||
|
||||
These tests exercise PartitionValidGptTable(), PartitionCheckGptEntry()
|
||||
and PartitionRestoreGptTable() against an in-memory mock disk, covering
|
||||
their behavior on well-formed GPT structures.
|
||||
|
||||
Copyright (c) 2026, SUSE LLC. All rights reserved.<BR>
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
#include <Uefi.h>
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/UnitTestLib.h>
|
||||
#include <Library/GptLib.h>
|
||||
#include "GptLibUnitTestCommon.h"
|
||||
|
||||
#define UNIT_TEST_NAME "GptLibUnitTest"
|
||||
#define UNIT_TEST_VERSION "1.0"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PartitionValidGptTable() tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Verify that a well-formed primary GPT header is accepted.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestValidPrimaryHeaderIsAccepted (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER Header;
|
||||
|
||||
SetupDiskWithValidPrimary ();
|
||||
|
||||
UT_ASSERT_TRUE (ValidateAt (PRIMARY_PART_HEADER_LBA, &Header));
|
||||
UT_ASSERT_EQUAL (Header.MyLBA, PRIMARY_PART_HEADER_LBA);
|
||||
UT_ASSERT_EQUAL (Header.AlternateLBA, LAST_LBA);
|
||||
UT_ASSERT_EQUAL (Header.NumberOfPartitionEntries, NUM_PARTITION_ENTRIES);
|
||||
UT_ASSERT_EQUAL (Header.SizeOfPartitionEntry, PARTITION_ENTRY_SIZE);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify that a well-formed backup GPT header is accepted.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestValidBackupHeaderIsAccepted (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER Header;
|
||||
|
||||
SetupDiskWithValidPrimary ();
|
||||
|
||||
//
|
||||
// Backup header at the last LBA with its entry array after the usable
|
||||
// region, as laid out by real partitioning tools. The validator must not
|
||||
// reject an entry array located past FirstUsableLBA.
|
||||
//
|
||||
WriteGptTableAt (
|
||||
LAST_LBA,
|
||||
PRIMARY_PART_HEADER_LBA,
|
||||
LAST_USABLE_LBA + 1,
|
||||
NUM_PARTITION_ENTRIES,
|
||||
PARTITION_ENTRY_SIZE,
|
||||
2
|
||||
);
|
||||
|
||||
UT_ASSERT_TRUE (ValidateAt (LAST_LBA, &Header));
|
||||
UT_ASSERT_EQUAL (Header.MyLBA, LAST_LBA);
|
||||
UT_ASSERT_EQUAL (Header.AlternateLBA, PRIMARY_PART_HEADER_LBA);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify that the minimum (92-byte) header size is accepted.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestMinimumHeaderSizeIsAccepted (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER Header;
|
||||
|
||||
SetupDiskWithValidPrimary ();
|
||||
|
||||
GetPrimaryHeader ()->Header.HeaderSize = TEST_GPT_HEADER_MIN_SIZE;
|
||||
UpdateGptHeaderCrc (GetPrimaryHeader ());
|
||||
|
||||
UT_ASSERT_TRUE (ValidateAt (PRIMARY_PART_HEADER_LBA, &Header));
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify that a larger power-of-two (256-byte) partition entry size is accepted.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestLargerPowerOfTwoEntrySizeIsAccepted (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER Header;
|
||||
|
||||
SetupDiskWithValidPrimary ();
|
||||
|
||||
//
|
||||
// SizeOfPartitionEntry of 256 (128 * 2^1) is legal per the UEFI spec.
|
||||
//
|
||||
WriteGptTableAt (PRIMARY_PART_HEADER_LBA, LAST_LBA, PRIMARY_PART_HEADER_LBA + 1, 8, 256, 2);
|
||||
|
||||
UT_ASSERT_TRUE (ValidateAt (PRIMARY_PART_HEADER_LBA, &Header));
|
||||
UT_ASSERT_EQUAL (Header.SizeOfPartitionEntry, 256U);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PartitionCheckGptEntry() tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Verify that valid partition entries report no flags.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestValidEntriesReportNoFlags (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER Header;
|
||||
EFI_PARTITION_ENTRY Entries[4];
|
||||
EFI_PARTITION_ENTRY_STATUS Status[4];
|
||||
UINTN Index;
|
||||
|
||||
InitCheckEntryHeader (&Header, 4, sizeof (EFI_PARTITION_ENTRY));
|
||||
ZeroMem (Entries, sizeof (Entries));
|
||||
ZeroMem (Status, sizeof (Status));
|
||||
|
||||
FillPartitionEntry (&Entries[0], &mPartitionGuid1, 34, 100);
|
||||
FillPartitionEntry (&Entries[1], &mPartitionGuid2, 101, 200);
|
||||
|
||||
PartitionCheckGptEntry (&Header, Entries, Status);
|
||||
|
||||
for (Index = 0; Index < 4; Index++) {
|
||||
UT_ASSERT_FALSE (Status[Index].OutOfRange);
|
||||
UT_ASSERT_FALSE (Status[Index].Overlap);
|
||||
UT_ASSERT_FALSE (Status[Index].OsSpecific);
|
||||
}
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify that an OS-specific partition attribute is reported.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestOsSpecificAttributeIsReported (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER Header;
|
||||
EFI_PARTITION_ENTRY Entries[1];
|
||||
EFI_PARTITION_ENTRY_STATUS Status[1];
|
||||
|
||||
InitCheckEntryHeader (&Header, 1, sizeof (EFI_PARTITION_ENTRY));
|
||||
ZeroMem (Entries, sizeof (Entries));
|
||||
ZeroMem (Status, sizeof (Status));
|
||||
|
||||
FillPartitionEntry (&Entries[0], &mPartitionGuid1, 40, 100);
|
||||
Entries[0].Attributes = BIT1;
|
||||
|
||||
PartitionCheckGptEntry (&Header, Entries, Status);
|
||||
|
||||
UT_ASSERT_TRUE (Status[0].OsSpecific);
|
||||
UT_ASSERT_FALSE (Status[0].OutOfRange);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify that unused partition entries are ignored.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestUnusedEntriesAreIgnored (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER Header;
|
||||
EFI_PARTITION_ENTRY Entries[2];
|
||||
EFI_PARTITION_ENTRY_STATUS Status[2];
|
||||
|
||||
InitCheckEntryHeader (&Header, 2, sizeof (EFI_PARTITION_ENTRY));
|
||||
ZeroMem (Entries, sizeof (Entries));
|
||||
ZeroMem (Status, sizeof (Status));
|
||||
|
||||
//
|
||||
// Unused (zero PartitionTypeGUID) entry with nonsense LBAs must be skipped
|
||||
// and must not participate in the overlap scan.
|
||||
//
|
||||
Entries[0].StartingLBA = MAX_UINT64;
|
||||
Entries[0].EndingLBA = 0;
|
||||
FillPartitionEntry (&Entries[1], &mPartitionGuid1, 40, 100);
|
||||
|
||||
PartitionCheckGptEntry (&Header, Entries, Status);
|
||||
|
||||
UT_ASSERT_FALSE (Status[0].OutOfRange);
|
||||
UT_ASSERT_FALSE (Status[0].Overlap);
|
||||
UT_ASSERT_FALSE (Status[1].OutOfRange);
|
||||
UT_ASSERT_FALSE (Status[1].Overlap);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify that the entry stride follows SizeOfPartitionEntry.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestEntryStrideFollowsSizeOfPartitionEntry (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER Header;
|
||||
UINT8 Buffer[2 * 256];
|
||||
EFI_PARTITION_ENTRY_STATUS Status[2];
|
||||
EFI_PARTITION_ENTRY *DecoyEntry;
|
||||
|
||||
InitCheckEntryHeader (&Header, 2, 256);
|
||||
ZeroMem (Buffer, sizeof (Buffer));
|
||||
ZeroMem (Status, sizeof (Status));
|
||||
|
||||
//
|
||||
// Real entries at 256-byte stride: entry 0 at offset 0, entry 1 at offset
|
||||
// 256, deliberately overlapping. A decoy non-overlapping but out-of-range
|
||||
// entry is placed at offset 128: a checker that wrongly walked with a
|
||||
// 128-byte stride would flag OutOfRange instead of Overlap on index 1.
|
||||
//
|
||||
FillPartitionEntry ((EFI_PARTITION_ENTRY *)(VOID *)&Buffer[0], &mPartitionGuid1, 40, 100);
|
||||
FillPartitionEntry ((EFI_PARTITION_ENTRY *)(VOID *)&Buffer[256], &mPartitionGuid2, 90, 150);
|
||||
DecoyEntry = (EFI_PARTITION_ENTRY *)(VOID *)&Buffer[128];
|
||||
FillPartitionEntry (DecoyEntry, &mPartitionGuid1, 1000, 2000);
|
||||
|
||||
PartitionCheckGptEntry (&Header, (EFI_PARTITION_ENTRY *)(VOID *)Buffer, Status);
|
||||
|
||||
UT_ASSERT_TRUE (Status[0].Overlap);
|
||||
UT_ASSERT_TRUE (Status[1].Overlap);
|
||||
UT_ASSERT_FALSE (Status[1].OutOfRange);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PartitionRestoreGptTable() tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Verify that a corrupted primary GPT is restored from the backup.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestRestorePrimaryFromBackup (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER BackupHeader;
|
||||
EFI_PARTITION_TABLE_HEADER RestoredHeader;
|
||||
|
||||
SetupDiskWithValidPrimary ();
|
||||
|
||||
//
|
||||
// Wipe the primary and keep only a valid backup whose entry array follows
|
||||
// the usable region.
|
||||
//
|
||||
WriteGptTableAt (
|
||||
LAST_LBA,
|
||||
PRIMARY_PART_HEADER_LBA,
|
||||
LAST_USABLE_LBA + 1,
|
||||
NUM_PARTITION_ENTRIES,
|
||||
PARTITION_ENTRY_SIZE,
|
||||
2
|
||||
);
|
||||
ZeroMem (GetDiskLba (PRIMARY_PART_HEADER_LBA), SECTOR_SIZE);
|
||||
ZeroMem (GetDiskLba (PRIMARY_PART_HEADER_LBA + 1), PART_ARRAY_SIZE);
|
||||
|
||||
UT_ASSERT_FALSE (ValidateAt (PRIMARY_PART_HEADER_LBA, &RestoredHeader));
|
||||
UT_ASSERT_TRUE (ValidateAt (LAST_LBA, &BackupHeader));
|
||||
|
||||
UT_ASSERT_TRUE (PartitionRestoreGptTable (&mBlockIo, &mDiskIo, &BackupHeader));
|
||||
|
||||
UT_ASSERT_TRUE (ValidateAt (PRIMARY_PART_HEADER_LBA, &RestoredHeader));
|
||||
UT_ASSERT_EQUAL (RestoredHeader.MyLBA, PRIMARY_PART_HEADER_LBA);
|
||||
UT_ASSERT_EQUAL (RestoredHeader.AlternateLBA, LAST_LBA);
|
||||
UT_ASSERT_EQUAL (RestoredHeader.PartitionEntryLBA, PRIMARY_PART_HEADER_LBA + 1);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify that a corrupted backup GPT is restored from the primary.
|
||||
|
||||
@param[in] Context Unit test context.
|
||||
|
||||
@retval UNIT_TEST_PASSED The test passed.
|
||||
**/
|
||||
STATIC
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestRestoreBackupFromPrimary (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER PrimaryHeader;
|
||||
EFI_PARTITION_TABLE_HEADER RestoredHeader;
|
||||
|
||||
SetupDiskWithValidPrimary ();
|
||||
|
||||
UT_ASSERT_TRUE (ValidateAt (PRIMARY_PART_HEADER_LBA, &PrimaryHeader));
|
||||
UT_ASSERT_FALSE (ValidateAt (LAST_LBA, &RestoredHeader));
|
||||
|
||||
UT_ASSERT_TRUE (PartitionRestoreGptTable (&mBlockIo, &mDiskIo, &PrimaryHeader));
|
||||
|
||||
UT_ASSERT_TRUE (ValidateAt (LAST_LBA, &RestoredHeader));
|
||||
UT_ASSERT_EQUAL (RestoredHeader.MyLBA, LAST_LBA);
|
||||
UT_ASSERT_EQUAL (RestoredHeader.AlternateLBA, PRIMARY_PART_HEADER_LBA);
|
||||
UT_ASSERT_EQUAL (RestoredHeader.PartitionEntryLBA, LAST_USABLE_LBA + 1);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test runner
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Configure and run the GptLib unit test suites.
|
||||
|
||||
@retval EFI_SUCCESS The unit tests ran to completion.
|
||||
@retval other An error occurred setting up or running the tests.
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
UefiTestMain (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UNIT_TEST_FRAMEWORK_HANDLE Framework;
|
||||
UNIT_TEST_SUITE_HANDLE ValidSuite;
|
||||
UNIT_TEST_SUITE_HANDLE EntrySuite;
|
||||
UNIT_TEST_SUITE_HANDLE RestoreSuite;
|
||||
|
||||
Framework = NULL;
|
||||
|
||||
Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = CreateUnitTestSuite (&ValidSuite, Framework, "PartitionValidGptTable tests", "GptLib.ValidGptTable", NULL, NULL);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Status = CreateUnitTestSuite (&EntrySuite, Framework, "PartitionCheckGptEntry tests", "GptLib.CheckGptEntry", NULL, NULL);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Status = CreateUnitTestSuite (&RestoreSuite, Framework, "PartitionRestoreGptTable tests", "GptLib.RestoreGptTable", NULL, NULL);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
AddTestCase (ValidSuite, "Valid primary header is accepted", "ValidPrimary", TestValidPrimaryHeaderIsAccepted, NULL, NULL, NULL);
|
||||
AddTestCase (ValidSuite, "Valid backup header is accepted", "ValidBackup", TestValidBackupHeaderIsAccepted, NULL, NULL, NULL);
|
||||
AddTestCase (ValidSuite, "Minimum (92-byte) header size is accepted", "MinHeaderSize", TestMinimumHeaderSizeIsAccepted, NULL, NULL, NULL);
|
||||
AddTestCase (ValidSuite, "256-byte partition entry size is accepted", "EntrySize256", TestLargerPowerOfTwoEntrySizeIsAccepted, NULL, NULL, NULL);
|
||||
|
||||
AddTestCase (EntrySuite, "Valid entries report no flags", "ValidEntries", TestValidEntriesReportNoFlags, NULL, NULL, NULL);
|
||||
AddTestCase (EntrySuite, "OS-specific attribute is reported", "OsSpecificAttribute", TestOsSpecificAttributeIsReported, NULL, NULL, NULL);
|
||||
AddTestCase (EntrySuite, "Unused entries are ignored", "UnusedEntriesIgnored", TestUnusedEntriesAreIgnored, NULL, NULL, NULL);
|
||||
AddTestCase (EntrySuite, "Entry stride follows SizeOfPartitionEntry", "EntryStride", TestEntryStrideFollowsSizeOfPartitionEntry, NULL, NULL, NULL);
|
||||
|
||||
AddTestCase (RestoreSuite, "Restore primary from backup", "RestorePrimary", TestRestorePrimaryFromBackup, NULL, NULL, NULL);
|
||||
AddTestCase (RestoreSuite, "Restore backup from primary", "RestoreBackup", TestRestoreBackupFromPrimary, NULL, NULL, NULL);
|
||||
|
||||
Status = RunAllTestSuites (Framework);
|
||||
|
||||
Exit:
|
||||
if (Framework != NULL) {
|
||||
FreeUnitTestFramework (Framework);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
///
|
||||
/// Avoid ECC error for function name that starts with lower case letter
|
||||
///
|
||||
#define GptLibUnitTestMain main
|
||||
|
||||
/**
|
||||
Standard POSIX C entry point for host based unit test execution.
|
||||
|
||||
@param[in] Argc Number of arguments
|
||||
@param[in] Argv Array of pointers to arguments
|
||||
|
||||
@retval 0 Success
|
||||
@retval other Error
|
||||
**/
|
||||
INT32
|
||||
GptLibUnitTestMain (
|
||||
IN INT32 Argc,
|
||||
IN CHAR8 *Argv[]
|
||||
)
|
||||
{
|
||||
UefiTestMain ();
|
||||
return 0;
|
||||
}
|
||||
298
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTestCommon.c
Normal file
298
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTestCommon.c
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/** @file
|
||||
Shared mock disk and GPT construction helpers used by the GptLib
|
||||
host-based unit tests.
|
||||
|
||||
Copyright (c) 2026, SUSE LLC. All rights reserved.<BR>
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include "GptLibUnitTestCommon.h"
|
||||
|
||||
UINT8 mDiskImage[DISK_IMAGE_SIZE];
|
||||
|
||||
EFI_BLOCK_IO_MEDIA mBlockIoMedia;
|
||||
EFI_BLOCK_IO_PROTOCOL mBlockIo;
|
||||
EFI_DISK_IO_PROTOCOL mDiskIo;
|
||||
BOOLEAN mWriteProtected;
|
||||
|
||||
CONST EFI_GUID mEspTypeGuid = {
|
||||
0xC12A7328, 0xF81F, 0x11D2, { 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B }
|
||||
};
|
||||
|
||||
CONST EFI_GUID mPartitionGuid1 = {
|
||||
0x11111111, 0x2222, 0x3333, { 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77 }
|
||||
};
|
||||
|
||||
CONST EFI_GUID mPartitionGuid2 = {
|
||||
0x88888888, 0x9999, 0xAAAA, { 0xBB, 0xBB, 0xCC, 0xCC, 0xDD, 0xDD, 0xEE, 0xEE }
|
||||
};
|
||||
|
||||
/**
|
||||
Mock implementation of EFI_DISK_IO_PROTOCOL.ReadDisk backed by the in-memory
|
||||
disk image.
|
||||
|
||||
@param[in] This Pointer to the EFI_DISK_IO_PROTOCOL instance.
|
||||
@param[in] MediaId ID of the medium to read from.
|
||||
@param[in] Offset Starting byte offset on the logical disk.
|
||||
@param[in] BufferSize Number of bytes to read.
|
||||
@param[out] Buffer Buffer into which the data is read.
|
||||
|
||||
@retval EFI_SUCCESS The data was read successfully.
|
||||
@retval EFI_DEVICE_ERROR The request was invalid or out of range.
|
||||
**/
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
MockReadDisk (
|
||||
IN EFI_DISK_IO_PROTOCOL *This,
|
||||
IN UINT32 MediaId,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN BufferSize,
|
||||
OUT VOID *Buffer
|
||||
)
|
||||
{
|
||||
if ((Buffer == NULL) ||
|
||||
(MediaId != mBlockIoMedia.MediaId) ||
|
||||
(Offset > DISK_IMAGE_SIZE) ||
|
||||
(BufferSize > (DISK_IMAGE_SIZE - Offset)))
|
||||
{
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
CopyMem (Buffer, mDiskImage + Offset, BufferSize);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Mock implementation of EFI_DISK_IO_PROTOCOL.WriteDisk backed by the in-memory
|
||||
disk image, honoring the write-protected flag.
|
||||
|
||||
@param[in] This Pointer to the EFI_DISK_IO_PROTOCOL instance.
|
||||
@param[in] MediaId ID of the medium to write to.
|
||||
@param[in] Offset Starting byte offset on the logical disk.
|
||||
@param[in] BufferSize Number of bytes to write.
|
||||
@param[in] Buffer Buffer holding the data to write.
|
||||
|
||||
@retval EFI_SUCCESS The data was written successfully.
|
||||
@retval EFI_WRITE_PROTECTED The mock medium is write-protected.
|
||||
@retval EFI_DEVICE_ERROR The request was invalid or out of range.
|
||||
**/
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
MockWriteDisk (
|
||||
IN EFI_DISK_IO_PROTOCOL *This,
|
||||
IN UINT32 MediaId,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN BufferSize,
|
||||
IN VOID *Buffer
|
||||
)
|
||||
{
|
||||
if (mWriteProtected) {
|
||||
return EFI_WRITE_PROTECTED;
|
||||
}
|
||||
|
||||
if ((Buffer == NULL) ||
|
||||
(MediaId != mBlockIoMedia.MediaId) ||
|
||||
(Offset > DISK_IMAGE_SIZE) ||
|
||||
(BufferSize > (DISK_IMAGE_SIZE - Offset)))
|
||||
{
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
CopyMem (mDiskImage + Offset, Buffer, BufferSize);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Return a pointer into the in-memory disk image at the given LBA.
|
||||
|
||||
@param[in] Lba Logical block address to locate.
|
||||
|
||||
@return Pointer to the start of the requested block within the disk image.
|
||||
**/
|
||||
UINT8 *
|
||||
GetDiskLba (
|
||||
IN EFI_LBA Lba
|
||||
)
|
||||
{
|
||||
return mDiskImage + (UINTN)(Lba * SECTOR_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
Return a pointer to the primary GPT header within the in-memory disk image.
|
||||
|
||||
@return Pointer to the primary EFI_PARTITION_TABLE_HEADER.
|
||||
**/
|
||||
EFI_PARTITION_TABLE_HEADER *
|
||||
GetPrimaryHeader (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
return (EFI_PARTITION_TABLE_HEADER *)(VOID *)GetDiskLba (PRIMARY_PART_HEADER_LBA);
|
||||
}
|
||||
|
||||
/**
|
||||
Recompute and store the header CRC32 for the given GPT header.
|
||||
|
||||
@param[in,out] Header The GPT header whose CRC32 field is updated.
|
||||
**/
|
||||
VOID
|
||||
UpdateGptHeaderCrc (
|
||||
IN OUT EFI_PARTITION_TABLE_HEADER *Header
|
||||
)
|
||||
{
|
||||
Header->Header.CRC32 = 0;
|
||||
Header->Header.CRC32 = CalculateCrc32 (Header, Header->Header.HeaderSize);
|
||||
}
|
||||
|
||||
/**
|
||||
Populate a partition entry with the ESP type GUID and the given attributes.
|
||||
|
||||
@param[in,out] Entry The partition entry to fill.
|
||||
@param[in] UniquePartitionGuid Unique partition GUID to assign.
|
||||
@param[in] StartingLba Starting LBA of the partition.
|
||||
@param[in] EndingLba Ending LBA of the partition.
|
||||
**/
|
||||
VOID
|
||||
FillPartitionEntry (
|
||||
IN OUT EFI_PARTITION_ENTRY *Entry,
|
||||
IN CONST EFI_GUID *UniquePartitionGuid,
|
||||
IN EFI_LBA StartingLba,
|
||||
IN EFI_LBA EndingLba
|
||||
)
|
||||
{
|
||||
CopyGuid (&Entry->PartitionTypeGUID, &mEspTypeGuid);
|
||||
CopyGuid (&Entry->UniquePartitionGUID, UniquePartitionGuid);
|
||||
Entry->StartingLBA = StartingLba;
|
||||
Entry->EndingLBA = EndingLba;
|
||||
}
|
||||
|
||||
/**
|
||||
Construct a complete GPT header and partition entry array at the given LBA in
|
||||
the in-memory disk image, recalculating all CRCs.
|
||||
|
||||
@param[in] HeaderLba LBA at which to write the GPT header.
|
||||
@param[in] AlternateLba Value stored in the header AlternateLBA field.
|
||||
@param[in] EntryArrayLba LBA at which to write the partition entry array.
|
||||
@param[in] NumEntries Number of partition entries in the array.
|
||||
@param[in] EntrySize Size in bytes of each partition entry.
|
||||
@param[in] NumPartitions Number of populated partition entries.
|
||||
**/
|
||||
VOID
|
||||
WriteGptTableAt (
|
||||
IN EFI_LBA HeaderLba,
|
||||
IN EFI_LBA AlternateLba,
|
||||
IN EFI_LBA EntryArrayLba,
|
||||
IN UINT32 NumEntries,
|
||||
IN UINT32 EntrySize,
|
||||
IN UINT32 NumPartitions
|
||||
)
|
||||
{
|
||||
EFI_PARTITION_TABLE_HEADER *Header;
|
||||
UINT8 *Array;
|
||||
UINT32 ArraySize;
|
||||
|
||||
ArraySize = NumEntries * EntrySize;
|
||||
Array = GetDiskLba (EntryArrayLba);
|
||||
ZeroMem (Array, ArraySize);
|
||||
if (NumPartitions >= 1) {
|
||||
FillPartitionEntry ((EFI_PARTITION_ENTRY *)(VOID *)Array, &mPartitionGuid1, 34, 133);
|
||||
}
|
||||
|
||||
if (NumPartitions >= 2) {
|
||||
FillPartitionEntry ((EFI_PARTITION_ENTRY *)(VOID *)(Array + EntrySize), &mPartitionGuid2, 134, 233);
|
||||
}
|
||||
|
||||
Header = (EFI_PARTITION_TABLE_HEADER *)(VOID *)GetDiskLba (HeaderLba);
|
||||
ZeroMem (Header, SECTOR_SIZE);
|
||||
Header->Header.Signature = EFI_PTAB_HEADER_ID;
|
||||
Header->Header.Revision = TEST_GPT_REVISION_V1;
|
||||
Header->Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER);
|
||||
Header->MyLBA = HeaderLba;
|
||||
Header->AlternateLBA = AlternateLba;
|
||||
Header->FirstUsableLBA = FIRST_USABLE_LBA;
|
||||
Header->LastUsableLBA = LAST_USABLE_LBA;
|
||||
Header->PartitionEntryLBA = EntryArrayLba;
|
||||
Header->NumberOfPartitionEntries = NumEntries;
|
||||
Header->SizeOfPartitionEntry = EntrySize;
|
||||
Header->PartitionEntryArrayCRC32 = CalculateCrc32 (Array, ArraySize);
|
||||
UpdateGptHeaderCrc (Header);
|
||||
}
|
||||
|
||||
/**
|
||||
Reset the mock disk and protocol state and lay down a valid primary GPT.
|
||||
**/
|
||||
VOID
|
||||
SetupDiskWithValidPrimary (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
ZeroMem (mDiskImage, sizeof (mDiskImage));
|
||||
|
||||
ZeroMem (&mBlockIoMedia, sizeof (mBlockIoMedia));
|
||||
mBlockIoMedia.MediaId = 1;
|
||||
mBlockIoMedia.BlockSize = SECTOR_SIZE;
|
||||
mBlockIoMedia.LastBlock = LAST_LBA;
|
||||
|
||||
ZeroMem (&mBlockIo, sizeof (mBlockIo));
|
||||
mBlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION;
|
||||
mBlockIo.Media = &mBlockIoMedia;
|
||||
|
||||
ZeroMem (&mDiskIo, sizeof (mDiskIo));
|
||||
mDiskIo.Revision = EFI_DISK_IO_PROTOCOL_REVISION;
|
||||
mDiskIo.ReadDisk = MockReadDisk;
|
||||
mDiskIo.WriteDisk = MockWriteDisk;
|
||||
|
||||
mWriteProtected = FALSE;
|
||||
|
||||
WriteGptTableAt (
|
||||
PRIMARY_PART_HEADER_LBA,
|
||||
LAST_LBA,
|
||||
PRIMARY_PART_HEADER_LBA + 1,
|
||||
NUM_PARTITION_ENTRIES,
|
||||
PARTITION_ENTRY_SIZE,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
Invoke PartitionValidGptTable against the mock disk at the given LBA.
|
||||
|
||||
@param[in] Lba LBA of the GPT header to validate.
|
||||
@param[out] Header Buffer to receive the validated GPT header.
|
||||
|
||||
@retval TRUE The GPT table at Lba is valid.
|
||||
@retval FALSE The GPT table at Lba is invalid.
|
||||
**/
|
||||
BOOLEAN
|
||||
ValidateAt (
|
||||
IN EFI_LBA Lba,
|
||||
OUT EFI_PARTITION_TABLE_HEADER *Header
|
||||
)
|
||||
{
|
||||
return PartitionValidGptTable (&mBlockIo, &mDiskIo, Lba, Header);
|
||||
}
|
||||
|
||||
/**
|
||||
Initialize a GPT header in memory for PartitionCheckGptEntry tests.
|
||||
|
||||
@param[out] Header The GPT header to initialize.
|
||||
@param[in] NumEntries Number of partition entries to record.
|
||||
@param[in] EntrySize Size in bytes of each partition entry.
|
||||
**/
|
||||
VOID
|
||||
InitCheckEntryHeader (
|
||||
OUT EFI_PARTITION_TABLE_HEADER *Header,
|
||||
IN UINT32 NumEntries,
|
||||
IN UINT32 EntrySize
|
||||
)
|
||||
{
|
||||
ZeroMem (Header, sizeof (*Header));
|
||||
Header->FirstUsableLBA = FIRST_USABLE_LBA;
|
||||
Header->LastUsableLBA = 200;
|
||||
Header->NumberOfPartitionEntries = NumEntries;
|
||||
Header->SizeOfPartitionEntry = EntrySize;
|
||||
}
|
||||
110
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTestCommon.h
Normal file
110
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTestCommon.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/** @file
|
||||
Shared mock disk and GPT construction helpers used by the GptLib
|
||||
host-based unit tests.
|
||||
|
||||
Copyright (c) 2026, SUSE LLC. All rights reserved.<BR>
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Uefi.h>
|
||||
#include <Protocol/BlockIo.h>
|
||||
#include <Protocol/DiskIo.h>
|
||||
#include <Library/GptLib.h>
|
||||
|
||||
#define SECTOR_SIZE 512U
|
||||
#define DISK_SECTORS 4096U
|
||||
#define DISK_IMAGE_SIZE (DISK_SECTORS * SECTOR_SIZE)
|
||||
#define LAST_LBA (DISK_SECTORS - 1U)
|
||||
#define NUM_PARTITION_ENTRIES 128U
|
||||
#define PARTITION_ENTRY_SIZE 128U
|
||||
#define PART_ARRAY_SIZE (NUM_PARTITION_ENTRIES * PARTITION_ENTRY_SIZE)
|
||||
#define FIRST_USABLE_LBA 34U
|
||||
#define LAST_USABLE_LBA (DISK_SECTORS - 34U)
|
||||
|
||||
//
|
||||
// The only GPT header revision defined by the UEFI specification, and the
|
||||
// minimum on-disk header size (through PartitionEntryArrayCRC32).
|
||||
//
|
||||
#define TEST_GPT_REVISION_V1 0x00010000U
|
||||
#define TEST_GPT_HEADER_MIN_SIZE 92U
|
||||
|
||||
extern UINT8 mDiskImage[DISK_IMAGE_SIZE];
|
||||
|
||||
extern EFI_BLOCK_IO_MEDIA mBlockIoMedia;
|
||||
extern EFI_BLOCK_IO_PROTOCOL mBlockIo;
|
||||
extern EFI_DISK_IO_PROTOCOL mDiskIo;
|
||||
extern BOOLEAN mWriteProtected;
|
||||
|
||||
extern CONST EFI_GUID mEspTypeGuid;
|
||||
extern CONST EFI_GUID mPartitionGuid1;
|
||||
extern CONST EFI_GUID mPartitionGuid2;
|
||||
|
||||
UINT8 *
|
||||
GetDiskLba (
|
||||
IN EFI_LBA Lba
|
||||
);
|
||||
|
||||
EFI_PARTITION_TABLE_HEADER *
|
||||
GetPrimaryHeader (
|
||||
VOID
|
||||
);
|
||||
|
||||
VOID
|
||||
UpdateGptHeaderCrc (
|
||||
IN OUT EFI_PARTITION_TABLE_HEADER *Header
|
||||
);
|
||||
|
||||
VOID
|
||||
FillPartitionEntry (
|
||||
IN OUT EFI_PARTITION_ENTRY *Entry,
|
||||
IN CONST EFI_GUID *UniquePartitionGuid,
|
||||
IN EFI_LBA StartingLba,
|
||||
IN EFI_LBA EndingLba
|
||||
);
|
||||
|
||||
/**
|
||||
Write a self-consistent GPT table (header plus partition entry array) to the
|
||||
mock disk at the requested locations, computing correct header and
|
||||
entry-array CRC32 values.
|
||||
|
||||
@param[in] HeaderLba LBA where the GPT header is written. Also stored
|
||||
in the header's MyLBA field.
|
||||
@param[in] AlternateLba Value stored in the header's AlternateLBA field.
|
||||
@param[in] EntryArrayLba LBA where the partition entry array is written.
|
||||
@param[in] NumEntries NumberOfPartitionEntries value.
|
||||
@param[in] EntrySize SizeOfPartitionEntry value.
|
||||
@param[in] NumPartitions Number of non-empty partition entries to populate.
|
||||
**/
|
||||
VOID
|
||||
WriteGptTableAt (
|
||||
IN EFI_LBA HeaderLba,
|
||||
IN EFI_LBA AlternateLba,
|
||||
IN EFI_LBA EntryArrayLba,
|
||||
IN UINT32 NumEntries,
|
||||
IN UINT32 EntrySize,
|
||||
IN UINT32 NumPartitions
|
||||
);
|
||||
|
||||
/**
|
||||
Reset the mock disk to a single valid primary GPT: header at LBA 1, a
|
||||
128 x 128-byte entry array at LBA 2 holding two partitions.
|
||||
**/
|
||||
VOID
|
||||
SetupDiskWithValidPrimary (
|
||||
VOID
|
||||
);
|
||||
|
||||
BOOLEAN
|
||||
ValidateAt (
|
||||
IN EFI_LBA Lba,
|
||||
OUT EFI_PARTITION_TABLE_HEADER *Header
|
||||
);
|
||||
|
||||
VOID
|
||||
InitCheckEntryHeader (
|
||||
OUT EFI_PARTITION_TABLE_HEADER *Header,
|
||||
IN UINT32 NumEntries,
|
||||
IN UINT32 EntrySize
|
||||
);
|
||||
40
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTestHost.inf
Normal file
40
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTestHost.inf
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
## @file
|
||||
# Host-based unit tests for GptLib.
|
||||
#
|
||||
# Exercises PartitionValidGptTable(), PartitionCheckGptEntry() and
|
||||
# PartitionRestoreGptTable() against an in-memory mock disk, covering their
|
||||
# behavior on well-formed GPT structures.
|
||||
#
|
||||
# Copyright (c) 2026, SUSE LLC. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
|
||||
[Defines]
|
||||
INF_VERSION = 0x00010006
|
||||
BASE_NAME = GptLibUnitTestHost
|
||||
FILE_GUID = 2F5B4C8E-6D3A-4B1F-9E07-8A2C5D14E6B3
|
||||
MODULE_TYPE = HOST_APPLICATION
|
||||
VERSION_STRING = 1.0
|
||||
|
||||
#
|
||||
# The following information is for reference only and not required by the build tools.
|
||||
#
|
||||
# VALID_ARCHITECTURES = IA32 X64
|
||||
#
|
||||
|
||||
[Sources]
|
||||
GptLibUnitTestCommon.h
|
||||
GptLibUnitTestCommon.c
|
||||
GptLibUnitTest.c
|
||||
|
||||
[Packages]
|
||||
MdePkg/MdePkg.dec
|
||||
MdeModulePkg/MdeModulePkg.dec
|
||||
UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
|
||||
|
||||
[LibraryClasses]
|
||||
BaseLib
|
||||
BaseMemoryLib
|
||||
DebugLib
|
||||
GptLib
|
||||
UnitTestLib
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
"8005", "UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGE.HID",
|
||||
"8001", "UefiSortLibUnitTestMain",
|
||||
"8001", "MediaSanitizeUnitTestMain",
|
||||
"8001", "GptLibUnitTestMain",
|
||||
],
|
||||
## Both file path and directory path are accepted.
|
||||
"IgnoreFiles": [
|
||||
|
|
|
|||
|
|
@ -77,6 +77,11 @@
|
|||
HobLib|MdePkg/Test/Mock/Library/GoogleTest/MockHobLib/MockHobLib.inf
|
||||
}
|
||||
|
||||
MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTestHost.inf {
|
||||
<LibraryClasses>
|
||||
GptLib|MdeModulePkg/Library/GptLib/GptLib.inf
|
||||
}
|
||||
|
||||
#
|
||||
# Build HOST_APPLICATION Libraries
|
||||
#
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue