blob: e839db3b908c4448006d179918fe8caf4e172b06 [file]
/** @file
Unit tests for PCI BAR parsing.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/GoogleTestLib.h>
extern "C" {
#include <Library/BaseMemoryLib.h>
#include "../PciBus.h"
}
typedef struct {
UINTN Offset;
UINT32 Value;
UINT32 OriginalValue;
} BAR_PROBE_RESULT;
STATIC BAR_PROBE_RESULT mProbeResults[PCI_MAX_BAR];
STATIC UINTN mProbeResultCount;
STATIC UINTN mProbeResultIndex;
STATIC BOOLEAN mProbeActive;
STATIC EFI_BOOT_SERVICES mBootServices;
extern "C" {
UINT64 gAllOne = MAX_UINT64;
EFI_BOOT_SERVICES *gBS = &mBootServices;
EFI_TPL
EFIAPI
TestRaiseTpl (
IN EFI_TPL NewTpl
)
{
EXPECT_EQ (NewTpl, (EFI_TPL)TPL_HIGH_LEVEL);
return TPL_APPLICATION;
}
VOID
EFIAPI
TestRestoreTpl (
IN EFI_TPL OldTpl
)
{
EXPECT_EQ (OldTpl, (EFI_TPL)TPL_APPLICATION);
}
EFI_STATUS
EFIAPI
TestPciRead (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT32 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
)
{
BAR_PROBE_RESULT *Result;
EXPECT_NE (This, nullptr);
EXPECT_EQ (Width, EfiPciIoWidthUint32);
EXPECT_EQ (Count, (UINTN)1);
if (mProbeResultIndex >= mProbeResultCount) {
ADD_FAILURE () << "Unexpected PCI configuration read";
return EFI_DEVICE_ERROR;
}
Result = &mProbeResults[mProbeResultIndex];
EXPECT_EQ (Offset, Result->Offset);
*(UINT32 *)Buffer = mProbeActive ? Result->Value : Result->OriginalValue;
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
TestPciWrite (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT32 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
)
{
BAR_PROBE_RESULT *Result;
UINT32 Value;
EXPECT_NE (This, nullptr);
EXPECT_EQ (Width, EfiPciIoWidthUint32);
EXPECT_EQ (Count, (UINTN)1);
if (mProbeResultIndex >= mProbeResultCount) {
ADD_FAILURE () << "Unexpected PCI configuration write";
return EFI_DEVICE_ERROR;
}
Result = &mProbeResults[mProbeResultIndex];
Value = *(UINT32 *)Buffer;
EXPECT_EQ (Offset, Result->Offset);
if (!mProbeActive) {
EXPECT_EQ (Value, MAX_UINT32);
mProbeActive = TRUE;
} else {
EXPECT_EQ (Value, Result->OriginalValue);
mProbeActive = FALSE;
mProbeResultIndex++;
}
return EFI_SUCCESS;
}
}
class PciParseBarTest : public ::testing::Test {
protected:
PCI_IO_DEVICE PciIoDevice;
void
SetUp (
) override
{
ZeroMem (&PciIoDevice, sizeof (PciIoDevice));
ZeroMem (mProbeResults, sizeof (mProbeResults));
ZeroMem (&mBootServices, sizeof (mBootServices));
mProbeResultCount = 0;
mProbeResultIndex = 0;
mProbeActive = FALSE;
mBootServices.RaiseTPL = TestRaiseTpl;
mBootServices.RestoreTPL = TestRestoreTpl;
PciIoDevice.PciIo.Pci.Read = TestPciRead;
PciIoDevice.PciIo.Pci.Write = TestPciWrite;
}
void
AddProbeResult (
UINTN Offset,
UINT32 Value,
UINT32 OriginalValue
)
{
ASSERT_LT (mProbeResultCount, ARRAY_SIZE (mProbeResults));
mProbeResults[mProbeResultCount++] = { Offset, Value, OriginalValue };
}
void
TearDown (
) override
{
EXPECT_FALSE (mProbeActive);
EXPECT_EQ (mProbeResultIndex, mProbeResultCount);
}
};
//
// PCI configuration:
//
// Register Offset Probe value Original value Interpretation
// -------- ------ ----------- -------------- ----------------
// BAR0 0x10 0x00000000 0x00000000 Not implemented
//
// Verify that BAR0 is cleared and parsing advances to BAR1 at offset 0x14.
//
TEST_F (PciParseBarTest, RecordsAbsentBarAndContinuesScanning) {
AddProbeResult (0x10, 0, 0);
EXPECT_EQ (PciParseBar (&PciIoDevice, 0x10), (UINTN)0x14);
EXPECT_EQ (PciIoDevice.PciBar[0].Offset, 0x10);
EXPECT_EQ (PciIoDevice.PciBar[0].BaseAddress, (UINT64)0);
EXPECT_EQ (PciIoDevice.PciBar[0].Length, (UINT64)0);
EXPECT_EQ (PciIoDevice.PciBar[0].Alignment, (UINT64)0);
}
//
// PCI configuration:
//
// Register Offset Probe value Original value Interpretation
// -------- ------ ----------- -------------- ------------------
// BAR0 0x10 0x0000FFE1 0x0000C001 16-bit I/O BAR
//
// Bit 0 selects I/O space. The probe mask describes a 0x20-byte resource
// based at 0xC000 with 0x1F alignment.
//
TEST_F (PciParseBarTest, ParsesIo16Bar) {
AddProbeResult (0x10, 0x0000FFE1, 0x0000C001);
EXPECT_EQ (PciParseBar (&PciIoDevice, 0x10), (UINTN)0x14);
EXPECT_EQ (PciIoDevice.PciBar[0].BarType, PciBarTypeIo16);
EXPECT_EQ (PciIoDevice.PciBar[0].BaseAddress, (UINT64)0xC000);
EXPECT_EQ (PciIoDevice.PciBar[0].Length, (UINT64)0x20);
EXPECT_EQ (PciIoDevice.PciBar[0].Alignment, (UINT64)0x1F);
}
//
// PCI configuration:
//
// Register Offset Probe value Original value Interpretation
// -------- ------ ----------- -------------- ------------------
// BAR0 0x10 0xFFFFFF01 0x12345001 32-bit I/O BAR
//
// Bit 0 selects I/O space. Implemented address bits above bit 15 select
// I/O32, and the probe mask describes a 0x100-byte resource at 0x12345000.
//
TEST_F (PciParseBarTest, ParsesIo32Bar) {
AddProbeResult (0x10, 0xFFFFFF01, 0x12345001);
EXPECT_EQ (PciParseBar (&PciIoDevice, 0x10), (UINTN)0x14);
EXPECT_EQ (PciIoDevice.PciBar[0].BarType, PciBarTypeIo32);
EXPECT_EQ (PciIoDevice.PciBar[0].BaseAddress, (UINT64)0x12345000);
EXPECT_EQ (PciIoDevice.PciBar[0].Length, (UINT64)0x100);
EXPECT_EQ (PciIoDevice.PciBar[0].Alignment, (UINT64)0xFF);
}
//
// PCI configuration:
//
// Register Offset Probe value Original value Interpretation
// -------- ------ ----------- -------------- ------------------
// BAR0 0x10 0xFFFFE008 0x80001008 Prefetchable MEM32
//
// Bit 3 marks the BAR prefetchable and bits 2:1 select a 32-bit memory BAR.
// The probe mask describes a 0x2000-byte resource based at 0x80001000.
//
TEST_F (PciParseBarTest, ParsesPrefetchableMemory32Bar) {
AddProbeResult (0x10, 0xFFFFE008, 0x80001008);
EXPECT_EQ (PciParseBar (&PciIoDevice, 0x10), (UINTN)0x14);
EXPECT_EQ (PciIoDevice.PciBar[0].BarType, PciBarTypePMem32);
EXPECT_EQ (PciIoDevice.PciBar[0].BaseAddress, (UINT64)0x80001000);
EXPECT_EQ (PciIoDevice.PciBar[0].Length, (UINT64)0x2000);
EXPECT_EQ (PciIoDevice.PciBar[0].Alignment, (UINT64)0x1FFF);
}
//
// PCI configuration:
//
// Register Offset Probe value Original value Interpretation
// -------- ------ ----------- -------------- ------------------
// BAR0 0x10 0xFFFFF004 0x34567004 MEM64 low DWORD
// BAR1 0x14 0xFFFFFFFF 0x00000012 MEM64 high DWORD
//
// Bits 2:1 in BAR0 select a 64-bit memory BAR. Verify that BAR0 and BAR1
// combine into base 0x0000001234567000 and a 0x1000-byte resource, and that
// parsing advances to BAR2 at offset 0x18.
//
TEST_F (PciParseBarTest, ParsesMemory64Bar) {
AddProbeResult (0x10, 0xFFFFF004, 0x34567004);
AddProbeResult (0x14, 0xFFFFFFFF, 0x00000012);
EXPECT_EQ (PciParseBar (&PciIoDevice, 0x10), (UINTN)0x18);
EXPECT_EQ (PciIoDevice.PciBar[0].BarType, PciBarTypeMem64);
EXPECT_EQ (PciIoDevice.PciBar[0].BaseAddress, 0x0000001234567000ULL);
EXPECT_EQ (PciIoDevice.PciBar[0].Length, (UINT64)0x1000);
EXPECT_EQ (PciIoDevice.PciBar[0].Alignment, (UINT64)0xFFF);
}
//
// PCI configuration:
//
// Register Offset Probe value Original value Interpretation
// -------- ------ ----------- -------------- ------------------
// BAR0 0x10 0xFFFFF004 0x34567004 MEM64 low DWORD
// BAR1 0x14 0xFFFFFFFF 0x00000012 MEM64 high DWORD
// BAR2 0x18 0xFFFFF000 0x80000000 MEM32
//
// BAR0 and BAR1 form one 64-bit resource. Verify that the independent
// resource at offset 0x18 is recorded as PciBar[2], leaving PciBar[1] empty
// because BAR1 is the upper DWORD rather than a separate resource.
//
TEST_F (PciParseBarTest, PreservesBarNumberAfterMemory64Bar) {
AddProbeResult (0x10, 0xFFFFF004, 0x34567004);
AddProbeResult (0x14, 0xFFFFFFFF, 0x00000012);
AddProbeResult (0x18, 0xFFFFF000, 0x80000000);
EXPECT_EQ (PciParseBar (&PciIoDevice, 0x10), (UINTN)0x18);
EXPECT_EQ (PciParseBar (&PciIoDevice, 0x18), (UINTN)0x1C);
EXPECT_EQ (PciIoDevice.PciBar[1].BarType, PciBarTypeUnknown);
EXPECT_EQ (PciIoDevice.PciBar[1].Length, (UINT64)0);
EXPECT_EQ (PciIoDevice.PciBar[2].Offset, 0x18);
EXPECT_EQ (PciIoDevice.PciBar[2].BarType, PciBarTypeMem32);
EXPECT_EQ (PciIoDevice.PciBar[2].BaseAddress, (UINT64)0x80000000);
EXPECT_EQ (PciIoDevice.PciBar[2].Length, (UINT64)0x1000);
}
class PciIovParseVfBarTest : public PciParseBarTest {
protected:
void
SetUp (
) override
{
PciParseBarTest::SetUp ();
PciIoDevice.SrIovCapabilityOffset = 0x100;
PciIoDevice.InitialVFs = 4;
PciIoDevice.SystemPageSize = SIZE_4KB;
}
};
//
// SR-IOV capability starts at PCI configuration offset 0x100:
//
// VF BAR Relative Absolute Probe value Original value
// ------- -------- -------- ----------- --------------
// VF BAR0 0x24 0x124 0x00000000 0x00000000
//
// A zero probe value means VF BAR0 is not implemented. Verify that its entry
// is cleared and parsing advances to VF BAR1 at absolute offset 0x128.
//
TEST_F (PciIovParseVfBarTest, RecordsAbsentVfBarAndContinuesScanning) {
AddProbeResult (0x124, 0, 0);
EXPECT_EQ (PciIovParseVfBar (&PciIoDevice, 0x124), (UINTN)0x128);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Offset, 0x124);
EXPECT_EQ (PciIoDevice.VfPciBar[0].BaseAddress, (UINT64)0);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Length, (UINT64)0);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Alignment, (UINT64)0);
}
//
// SR-IOV capability starts at PCI configuration offset 0x100:
//
// VF BAR Relative Absolute Probe value Original value
// ------- -------- -------- ----------- --------------
// VF BAR0 0x24 0x124 0xFFFFF000 0x80000000
//
// VF BAR0 is a 0x1000-byte MEM32 BAR. With InitialVFs set to 4, verify that
// the aggregate resource length is 0x4000 and that its alignment is 0xFFF
// for the selected 4-KB SR-IOV system page size.
//
TEST_F (PciIovParseVfBarTest, ParsesMemory32VfBar) {
AddProbeResult (0x124, 0xFFFFF000, 0x80000000);
EXPECT_EQ (PciIovParseVfBar (&PciIoDevice, 0x124), (UINTN)0x128);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Offset, 0x124);
EXPECT_EQ (PciIoDevice.VfPciBar[0].BarType, PciBarTypeMem32);
EXPECT_EQ (PciIoDevice.VfPciBar[0].BaseAddress, (UINT64)0x80000000);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Length, (UINT64)0x4000);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Alignment, (UINT64)0xFFF);
}
//
// SR-IOV capability starts at PCI configuration offset 0x100:
//
// VF BAR Relative Absolute Probe value Original value
// ------- -------- -------- ----------- --------------
// VF BAR0 0x24 0x124 0xFFFFF004 0x34567004
// VF BAR1 0x28 0x128 0xFFFFFFFF 0x00000012
//
// Bits 2:1 in VF BAR0 select a 64-bit memory BAR. Verify that both DWORDs
// combine into base 0x0000001234567000, that the per-VF 0x1000-byte size is
// scaled to 0x4000 for four VFs, and that parsing advances to VF BAR2.
//
TEST_F (PciIovParseVfBarTest, ParsesMemory64VfBar) {
AddProbeResult (0x124, 0xFFFFF004, 0x34567004);
AddProbeResult (0x128, 0xFFFFFFFF, 0x00000012);
EXPECT_EQ (PciIovParseVfBar (&PciIoDevice, 0x124), (UINTN)0x12C);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Offset, 0x124);
EXPECT_EQ (PciIoDevice.VfPciBar[0].BarType, PciBarTypeMem64);
EXPECT_EQ (PciIoDevice.VfPciBar[0].BaseAddress, 0x0000001234567000ULL);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Length, (UINT64)0x4000);
EXPECT_EQ (PciIoDevice.VfPciBar[0].Alignment, (UINT64)0xFFF);
}
//
// SR-IOV capability starts at PCI configuration offset 0x100:
//
// VF BAR Relative Absolute Probe value Original value
// ------- -------- -------- ----------- --------------
// VF BAR0 0x24 0x124 0xFFFFF004 0x34567004
// VF BAR1 0x28 0x128 0xFFFFFFFF 0x00000012
// VF BAR2 0x2C 0x12C 0xFFFFF000 0x80000000
//
// VF BAR0 and VF BAR1 form one 64-bit resource. Verify that the independent
// MEM32 resource at relative offset 0x2C is recorded as VfPciBar[2], leaving
// VfPciBar[1] empty because VF BAR1 is the upper DWORD.
//
TEST_F (PciIovParseVfBarTest, PreservesVfBarNumberAfterMemory64Bar) {
AddProbeResult (0x124, 0xFFFFF004, 0x34567004);
AddProbeResult (0x128, 0xFFFFFFFF, 0x00000012);
AddProbeResult (0x12C, 0xFFFFF000, 0x80000000);
EXPECT_EQ (PciIovParseVfBar (&PciIoDevice, 0x124), (UINTN)0x12C);
EXPECT_EQ (PciIovParseVfBar (&PciIoDevice, 0x12C), (UINTN)0x130);
EXPECT_EQ (PciIoDevice.VfPciBar[1].BarType, PciBarTypeUnknown);
EXPECT_EQ (PciIoDevice.VfPciBar[1].Length, (UINT64)0);
EXPECT_EQ (PciIoDevice.VfPciBar[2].Offset, 0x12C);
EXPECT_EQ (PciIoDevice.VfPciBar[2].BarType, PciBarTypeMem32);
EXPECT_EQ (PciIoDevice.VfPciBar[2].BaseAddress, (UINT64)0x80000000);
EXPECT_EQ (PciIoDevice.VfPciBar[2].Length, (UINT64)0x4000);
}
int
main (
int argc,
char *argv[]
)
{
testing::InitGoogleTest (&argc, argv);
return RUN_ALL_TESTS ();
}