Add polymorphic cper encoder class
This class is intended to be the base of other CperEncoders, the
mockEncoder is a generic example of how this would look.
Change-Id: Ic0126a767ca8c2827ae32b6716c27c3b42803c5a
Signed-off-by: Aryk Ledet <arykledet@google.com>
diff --git a/README.md b/README.md
index d1e877b..548cd57 100644
--- a/README.md
+++ b/README.md
@@ -13,3 +13,49 @@
gBMC applications to log faults in the CPER format the user only needs to
specify the type of Record and Section type(s) that the encoder will need to
create.
+
+When developing and testing your application specific encoders, you will need to
+install this library into your development container.
+
+Below is some pseudo code of how a user would setup their application specific
+CPER encoder which takes in char array types.
+
+```c++
+class MyCperEncoder : public CperEncoder<char>
+{
+ RecordHeader createRecordHeader(...) override
+ {...}
+
+ SectionDescriptor createSectionDescriptor(...) override
+ {
+ if (sectionType == kPlatformMemory)
+ {
+ return createPlatformMemoryDescriptor();
+ }
+ else if (sectionType == kOemSection)
+ {
+ return createOemDescriptor();
+ }
+ ...
+ }
+
+ SectionDescriptor createPlatformMemoryDescriptor()
+ {...}
+
+ SectionDescriptor createOemDescriptor()
+ {...}
+}
+```
+
+## To install
+
+```sh
+meson setup -C builddir
+meson install -C builddir
+```
+
+## To run unit tests
+
+```sh
+meson test -C builddir
+```
diff --git a/include/cper_encoder.hpp b/include/cper_encoder.hpp
index 20df915..aea2743 100644
--- a/include/cper_encoder.hpp
+++ b/include/cper_encoder.hpp
@@ -4,6 +4,9 @@
#include <chrono>
#include <concepts>
+#include <cstdint>
+#include <cstring>
+#include <string>
namespace uefi::cper
{
@@ -95,4 +98,167 @@
std::span<ByteType> baseDest_;
uint64_t offset_;
};
+
+template <ByteLike BodyType>
+class CperEncoder
+{
+ public:
+ CperEncoder() : totalSectionBodySizeBytes_(0)
+ {}
+
+ /**
+ * @brief Implementation specific CPER encoders must define how to create
+ * RecordHeaders.
+ * https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html#record-header
+ *
+ * @param[in] recordLength - The length in bytes of the entire record.
+ * @param[in] sectionCount - The number of sections in the record.
+ */
+ virtual RecordHeader createRecordHeader(const uint32_t recordLength,
+ const uint16_t sectionCount) = 0;
+
+ /**
+ * @brief Implementation specific CPER encoders must define how to create
+ * new sectionDescriptors.
+ * https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html#section-descriptor
+ *
+ * @param[in] sectionLength - The length in bytes of the section body.
+ * @param[in] sectionOffset - The offset in bytes from the section body from
+ * the base of the record header.
+ * @param[in] sectionFlags - Information that describes the error section.
+ * @param[in] sectionType - The ID of the sectionType; useful for
+ * implementations with multiple section types.
+ *
+ * @return A SectionDescriptor struct.
+ */
+ virtual SectionDescriptor createSectionDescriptor(
+ const uint32_t sectionLength, const uint32_t sectionOffset,
+ const uint32_t sectionFlags, const guid_t sectionType) = 0;
+
+ /**
+ * @brief Adds a new CPER section for the encoder to track.
+ *
+ * @note Cper logs have a max size of 4GiB including the header and
+ * descriptors.
+ *
+ * @param[in] sectionBody - A span containing the section data as a byte
+ * array.
+ * @param[in] sectionFlags - The CPER flags for the section.
+ * @param[in] sectionType - The guid_t for the section.
+ *
+ * @warning The CperEncoder class does not own the ptrs of the
+ * sectionBodies, it merely tracks the addresses and size. It is expected
+ * that the data added to the encoder exists for the entire lifetime of the
+ * CperEncoder object.
+ *
+ * @returns true on success, false if new section goes over the 4GiB
+ * threshold.
+ */
+ bool addSection(const std::span<const BodyType> sectionBody,
+ const uint32_t sectionFlags, const guid_t sectionType)
+ {
+ if ((calculateRecordLengthBytes() + sectionBody.size_bytes() +
+ kSectionDescriptorSizeBytes) >
+ std::numeric_limits<uint32_t>::max())
+ {
+ return false;
+ }
+
+ sectionSeeds_.emplace_back(sectionFlags, sectionType, sectionBody);
+ totalSectionBodySizeBytes_ += sectionBody.size_bytes();
+ return true;
+ }
+
+ /**
+ * @brief Serializes the CPER Log into a vector of bytes.
+ *
+ * @return A byte array containing the CPER log contents.
+ */
+ std::vector<uint8_t> serializeToByteArray()
+ {
+ const uint64_t totalBytes = calculateRecordLengthBytes();
+ std::vector<uint8_t> byteArray(totalBytes);
+
+ encode<uint8_t>(byteArray);
+ return byteArray;
+ }
+
+ /**
+ * @brief Serializes the CPER Log into a string.
+ *
+ * @return A string containing the CPER log contents.
+ */
+ std::string serializeToString()
+ {
+ const uint64_t totalBytes = calculateRecordLengthBytes();
+ std::string str(totalBytes, 0);
+
+ encode<char>(str);
+ return str;
+ }
+
+ private:
+ // Save ~3x memory by only caching the section "seeds" and keeping a
+ // single full sectionDescriptor on the stack at a time while encoding
+ // the log.
+ struct SectionSeed
+ {
+ uint32_t sectionFlags;
+ guid_t sectionType;
+ std::span<const BodyType> body;
+ };
+
+ /**
+ * @brief Calculates the total size of the CPER log.
+ *
+ * @return The total size in bytes.
+ */
+ uint32_t calculateRecordLengthBytes() const
+ {
+ return kRecordHeaderSizeBytes +
+ (sectionSeeds_.size() * kSectionDescriptorSizeBytes) +
+ totalSectionBodySizeBytes_;
+ }
+
+ /**
+ * @brief Encodes the CPER byte array to the destination pointer.
+ *
+ * @tparam ByteType - Allows for different cpp byte representations (char,
+ * unsigned char, std::byte, etc.)
+ *
+ * @param[in,out] dest - The destination span to copy the data to.
+ *
+ * @throws If unable to successfully encode the CPER log.
+ */
+ template <ByteLike ByteType>
+ void encode(std::span<ByteType> dest)
+ {
+ SeqMemcopy seqMemcpy(dest);
+ {
+ const RecordHeader header =
+ createRecordHeader(dest.size_bytes(), sectionSeeds_.size());
+
+ seqMemcpy.copy(&header, kRecordHeaderSizeBytes);
+ }
+
+ uint64_t offset = 0;
+ for (const SectionSeed& seed : sectionSeeds_)
+ {
+ const SectionDescriptor descriptor =
+ createSectionDescriptor(seed.body.size_bytes(), offset,
+ seed.sectionFlags, seed.sectionType);
+ offset += seed.body.size_bytes();
+
+ seqMemcpy.copy(&descriptor, kSectionDescriptorSizeBytes);
+ }
+
+ for (const auto& seed : sectionSeeds_)
+ {
+ seqMemcpy.copy(seed.body.data(), seed.body.size_bytes());
+ }
+ }
+
+ uint32_t totalSectionBodySizeBytes_;
+ std::vector<SectionSeed> sectionSeeds_;
+};
} // namespace uefi::cper
diff --git a/test/cper_encoder_test.cpp b/test/cper_encoder_test.cpp
index c1e756f..f38cc72 100644
--- a/test/cper_encoder_test.cpp
+++ b/test/cper_encoder_test.cpp
@@ -1,18 +1,113 @@
#include "cper_encoder.hpp"
+#include "mock_cper_encoder.hpp"
#include <chrono>
+#include <concepts>
#include "gtest/gtest.h"
#include <gmock/gmock.h>
+using testing::_;
+using testing::Contains;
+
namespace uefi::cper
{
+template <typename T>
+concept StringOrVector =
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::vector<uint8_t>>;
+
class CperEncoderTest : public testing::Test
{
public:
void SetUp() override
{}
+ bool isArrayEq(const void* arr1, const void* arr2, const uint64_t arr1Size,
+ const uint64_t arr2Size)
+ {
+ if (arr1Size != arr2Size)
+ {
+ return false;
+ }
+ return memcmp(arr1, arr2, arr1Size) == 0;
+ }
+
+ template <StringOrVector CperType>
+ void expectValidHeader(const CperType& cperArray,
+ const uint16_t numSections, const uint64_t cperSize,
+ const uint64_t timestamp)
+ {
+ RecordHeader header;
+ memcpy(&header, cperArray.data(), sizeof(header));
+
+ EXPECT_EQ(header.signature, kRecordSignature);
+ EXPECT_EQ(header.revision, kRecordRevision);
+ EXPECT_EQ(header.signatureEnd, kRecordSignatureEnd);
+ EXPECT_EQ(header.sectionCount, numSections);
+ EXPECT_EQ(header.errorSeverity, kMockRecordSeverity);
+ EXPECT_EQ(header.validationBits, kMockValidationBits);
+ EXPECT_EQ(header.recordLength, cperSize);
+ EXPECT_EQ(header.timestamp, timestamp);
+ EXPECT_EQ(header.platformId, kMockPlatformId);
+ EXPECT_EQ(header.partitionId, kMockPartitionId);
+ EXPECT_EQ(header.creatorId, kMockCreatorId);
+ EXPECT_EQ(header.notificationType, kMockNotificationType);
+ EXPECT_EQ(header.recordId, kMockRecordId);
+ EXPECT_EQ(header.flags, kMockHeaderFlags);
+ EXPECT_EQ(header.persistenceInformation, kMockPersistenceInformation);
+ EXPECT_THAT(header.reserved, Contains(0).Times(header.reserved.size()));
+ }
+
+ template <StringOrVector CperType>
+ void expectValidSectionDescriptor(const CperType& cperArray,
+ const uint64_t sectionNum,
+ const uint32_t expectedOffset,
+ const uint32_t expectedLength,
+ const uint32_t expectedFlag,
+ const guid_t expectedSectionType)
+ {
+ const uint64_t sectionOffset =
+ kRecordHeaderSizeBytes + (kSectionDescriptorSizeBytes * sectionNum);
+
+ SectionDescriptor descriptor;
+ memcpy(&descriptor, &cperArray[sectionOffset], sizeof(descriptor));
+
+ EXPECT_EQ(descriptor.sectionOffset, expectedOffset);
+ EXPECT_EQ(descriptor.sectionLength, expectedLength);
+ EXPECT_EQ(descriptor.revision, kMockSectionRevision);
+ EXPECT_EQ(descriptor.validationBits, kMockSectionValidationBits);
+ EXPECT_EQ(descriptor.reserved, 0);
+ EXPECT_EQ(descriptor.flags, expectedFlag);
+ EXPECT_EQ(descriptor.sectionType, expectedSectionType);
+ EXPECT_EQ(descriptor.fruId, kMockFruId);
+ EXPECT_EQ(descriptor.sectionSeverity, kMockSectionSeverity);
+ EXPECT_EQ(descriptor.fruText, kMockFruText);
+ }
+
+ template <ByteLike BodyType, StringOrVector CperType>
+ void expectValidSectionBody(
+ const CperType& cperArray,
+ const std::span<const BodyType> expectedSectionBody,
+ const uint64_t totalSections, const uint32_t sectionOffset)
+ {
+ // The offset set in the section descriptors is the offset in bytes
+ // after the header and descriptors.
+ const uint32_t kTrueBodyOffset =
+ sectionOffset + kRecordHeaderSizeBytes +
+ (kSectionDescriptorSizeBytes * totalSections);
+
+ const uint64_t kSectionBodySize = expectedSectionBody.size();
+
+ ASSERT_GE(cperArray.size(), (kTrueBodyOffset + kSectionBodySize));
+
+ CperType sectionBody(kSectionBodySize, 0);
+ memcpy(sectionBody.data(), &cperArray[kTrueBodyOffset],
+ sectionBody.size());
+
+ EXPECT_TRUE(isArrayEq(sectionBody.data(), expectedSectionBody.data(),
+ sectionBody.size(), expectedSectionBody.size()));
+ }
+
void expectEqTimestamp(
const uint64_t cperTimestamp,
const std::chrono::time_point<std::chrono::system_clock> ref,
@@ -35,6 +130,207 @@
}
};
+TEST_F(CperEncoderTest, TooMuchData)
+{
+ MockCperEncoder<uint8_t> mockEncoder;
+ uint64_t totalSize = kRecordHeaderSizeBytes;
+
+ // Exceed the 4GiB CPER Log max size by adding 2^16 * 2^16 Bytes worth of
+ // section bodies along with their descriptors.
+ for (uint64_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++)
+ {
+ std::array<uint8_t, std::numeric_limits<uint16_t>::max()> sectionBody;
+ totalSize += sectionBody.size() + kSectionDescriptorSizeBytes;
+
+ // Note the encoder does not own the sectionBody, it just tracks the
+ // body ptrs and size. This would never work in a real application since
+ // the sectionBody only exists within each loops iteration.
+ if (totalSize < std::numeric_limits<uint32_t>::max())
+ {
+
+ EXPECT_TRUE(mockEncoder.addSection(sectionBody, kMockSectionFlag,
+ kMockSectionType));
+ }
+ else
+ {
+ EXPECT_FALSE(mockEncoder.addSection(sectionBody, kMockSectionFlag,
+ kMockSectionType));
+ return;
+ }
+ }
+
+ FAIL() << "Should never be able to complete the loop";
+}
+
+TEST_F(CperEncoderTest, EncodeMultipleCharSectionsToArrayAndString)
+{
+ MockCperEncoder<char> mockEncoder;
+
+ // Fill our CPER Log with some quotes to get through the work week.
+ std::vector<std::string> sectionBodies;
+ sectionBodies.push_back("The first principle is that you must not fool "
+ "yourself and you are the easiest person to fool.");
+ sectionBodies.push_back("Spread love everywhere you go.");
+ sectionBodies.push_back("The only thing we have to fear is fear itself.");
+ sectionBodies.push_back("If you can't explain it to a six year old, you "
+ "don't understand it yourself.");
+ sectionBodies.push_back("I have no wings, so I guess I'll look up at this "
+ "sky, and crawl along the Earth.");
+ sectionBodies.push_back("If I have seen further, it is by standing on the "
+ "shoulders of giants.");
+
+ const uint16_t kNumSections = sectionBodies.size();
+
+ // Add the sections to our encoder.
+ uint64_t totalSectionBodySize = 0;
+ for (const std::string& body : sectionBodies)
+ {
+ mockEncoder.addSection(body, kMockSectionFlag, kMockSectionType);
+ totalSectionBodySize += body.size();
+ }
+
+ const uint64_t kExpectedCperSize =
+ (kRecordHeaderSizeBytes + (kSectionDescriptorSizeBytes * kNumSections) +
+ totalSectionBodySize);
+
+ // Serialize to an array.
+ EXPECT_CALL(mockEncoder, mockCreateRecordHeader).Times(1);
+ EXPECT_CALL(mockEncoder, mockCreateSectionDescriptor).Times(kNumSections);
+ const std::vector<uint8_t> cperArray = mockEncoder.serializeToByteArray();
+ ASSERT_EQ(cperArray.size(), kExpectedCperSize);
+
+ // Check that the serialized arrays header contains the same contents we
+ // configured the encoder to use.
+ expectValidHeader(cperArray, kNumSections, kExpectedCperSize,
+ mockEncoder.timestamp);
+
+ // Check that the section descriptors and bodies all contain the correct
+ // information about their respective section.
+ uint32_t expectedOffset = 0;
+ uint64_t sectionIdx = 0;
+ for (const std::string& body : sectionBodies)
+ {
+ const uint32_t kBodyLength = body.size();
+ expectValidSectionDescriptor(cperArray, sectionIdx, expectedOffset,
+ kBodyLength, kMockSectionFlag,
+ kMockSectionType);
+
+ expectValidSectionBody<char>(cperArray, body, kNumSections,
+ expectedOffset);
+
+ expectedOffset += kBodyLength;
+ sectionIdx++;
+ }
+
+ // Serialize to a string.
+ EXPECT_CALL(mockEncoder, mockCreateRecordHeader).Times(1);
+ EXPECT_CALL(mockEncoder, mockCreateSectionDescriptor).Times(kNumSections);
+ const std::string cperStr = mockEncoder.serializeToString();
+ ASSERT_EQ(cperStr.size(), kExpectedCperSize);
+
+ // Check that the serialized arrays header contains the same contents we
+ // configured the encoder to use.
+ expectValidHeader(cperStr, kNumSections, kExpectedCperSize,
+ mockEncoder.timestamp);
+
+ // Check that the section descriptors and bodies all contain the correct
+ // information about their respective section.
+ expectedOffset = 0;
+ sectionIdx = 0;
+ for (const std::string& body : sectionBodies)
+ {
+ const uint32_t kBodyLength = body.size();
+ expectValidSectionDescriptor(cperStr, sectionIdx, expectedOffset,
+ kBodyLength, kMockSectionFlag,
+ kMockSectionType);
+
+ expectValidSectionBody<char>(cperStr, body, kNumSections,
+ expectedOffset);
+
+ expectedOffset += kBodyLength;
+ sectionIdx++;
+ }
+}
+
+TEST_F(CperEncoderTest, EncodeOneU8SectionToString)
+{
+ MockCperEncoder<uint8_t> mockEncoder;
+
+ const std::array<uint8_t, 10> sectionBody = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ const uint64_t kSectionBodySize = sectionBody.size();
+ constexpr uint16_t kNumSections = 1;
+
+ // Add 1 section
+ mockEncoder.addSection(sectionBody, kMockSectionFlag, kMockSectionType);
+
+ // Serialize the data into a CPER byte array.
+ EXPECT_CALL(mockEncoder, mockCreateRecordHeader).Times(1);
+ EXPECT_CALL(mockEncoder, mockCreateSectionDescriptor).Times(kNumSections);
+ const std::string cperArray = mockEncoder.serializeToString();
+
+ // If this bricks, abort ship - there is likely memory corruption.
+ const uint64_t kExpectedCperSize =
+ (kSectionBodySize + kSectionDescriptorSizeBytes +
+ kRecordHeaderSizeBytes);
+
+ ASSERT_EQ(cperArray.size(), kExpectedCperSize);
+
+ // Check that the serialized arrays header contains the same contents we
+ // configured the encoder to use.
+ expectValidHeader(cperArray, kNumSections, kExpectedCperSize,
+ mockEncoder.timestamp);
+
+ // Check that the section descriptor contains the correct information about
+ // the section.
+ const uint32_t kExpectedOffset = 0;
+ expectValidSectionDescriptor(cperArray, 0, kExpectedOffset,
+ kSectionBodySize, kMockSectionFlag,
+ kMockSectionType);
+
+ expectValidSectionBody<uint8_t>(cperArray, sectionBody, kNumSections,
+ kExpectedOffset);
+}
+
+TEST_F(CperEncoderTest, EncodeOneCharSectionToArray)
+{
+ MockCperEncoder<char> mockEncoder;
+
+ const std::string sectionBody =
+ "Hello world, this is a simplistic example of a CPER Section Body.";
+ const uint64_t kSectionBodySize = sectionBody.size();
+ constexpr uint16_t kNumSections = 1;
+
+ // Add 1 section
+ mockEncoder.addSection(sectionBody, kMockSectionFlag, kMockSectionType);
+
+ // Serialize the data into a CPER byte array.
+ EXPECT_CALL(mockEncoder, mockCreateRecordHeader).Times(1);
+ EXPECT_CALL(mockEncoder, mockCreateSectionDescriptor).Times(kNumSections);
+ const std::vector<uint8_t> cperArray = mockEncoder.serializeToByteArray();
+
+ // If this bricks, abort ship - there is likely memory corruption.
+ const uint64_t kExpectedCperSize =
+ (kSectionBodySize + kSectionDescriptorSizeBytes +
+ kRecordHeaderSizeBytes);
+
+ ASSERT_EQ(cperArray.size(), kExpectedCperSize);
+
+ // Check that the serialized arrays header contains the same contents we
+ // configured the encoder to use.
+ expectValidHeader(cperArray, kNumSections, kExpectedCperSize,
+ mockEncoder.timestamp);
+
+ // Check that the section descriptor contains the correct information about
+ // the section.
+ const uint32_t kExpectedOffset = 0;
+ expectValidSectionDescriptor(cperArray, 0, kExpectedOffset,
+ kSectionBodySize, kMockSectionFlag,
+ kMockSectionType);
+
+ expectValidSectionBody<char>(cperArray, sectionBody, kNumSections,
+ kExpectedOffset);
+}
+
TEST_F(CperEncoderTest, SeqMemcopy)
{
std::string section1 = "Hello";
diff --git a/test/mock_cper_encoder.hpp b/test/mock_cper_encoder.hpp
new file mode 100644
index 0000000..d314aeb
--- /dev/null
+++ b/test/mock_cper_encoder.hpp
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "cper_encoder.hpp"
+
+#include "gmock/gmock.h"
+
+namespace uefi::cper
+{
+
+// Record Header
+inline constexpr uint8_t kMockRecordSeverity =
+ static_cast<uint8_t>(ErrorSeverity::kFatal);
+inline constexpr uint32_t kMockValidationBits = 0x5;
+
+inline constexpr guid_t kMockPlatformId{
+ 0xF70B188B,
+ 0x8431,
+ 0x4AC0,
+ {0x85, 0x3B, 0x73, 0x44, 0x58, 0x82, 0x3E, 0xC5}};
+inline constexpr guid_t kMockPartitionId{
+ 0xCAFDBB26,
+ 0x7ED7,
+ 0x4D4E,
+ {0x82, 0xBA, 0x68, 0xB0, 0x91, 0x20, 0xD5, 0x0B}};
+inline constexpr guid_t kMockCreatorId{
+ 0x294C5454,
+ 0x6809,
+ 0x4C55,
+ {0xAD, 0x04, 0x82, 0x6B, 0xAD, 0xD8, 0x0A, 0x50}};
+
+inline constexpr guid_t kMockNotificationType = notification_type::kPEI;
+inline constexpr uint64_t kMockRecordId = 0xDEADBEEF;
+inline constexpr uint32_t kMockHeaderFlags = record_flags::kRecovered;
+inline constexpr uint64_t kMockPersistenceInformation = 0;
+
+// Section Descriptor
+inline constexpr uint16_t kMockSectionRevision = 0x0002;
+inline constexpr uint32_t kMockSectionValidationBits = 0x3;
+inline constexpr uint32_t kMockSectionFlag = 0x1;
+
+inline constexpr guid_t kMockSectionType{
+ 0xF0E2FCA7,
+ 0xAD2D,
+ 0x4B17,
+ {0x95, 0x0C, 0x76, 0x88, 0x7E, 0x6A, 0x30, 0xF3}};
+inline constexpr guid_t kMockFruId{
+ 0x1103ED84,
+ 0x62DD,
+ 0x4028,
+ {0xAE, 0x9D, 0x20, 0xCE, 0xF6, 0xE7, 0xB5, 0xA9}};
+
+inline constexpr uint8_t kMockSectionSeverity =
+ static_cast<uint8_t>(ErrorSeverity::kFatal);
+constexpr std::array<uint8_t, 20> kMockFruText = {'m', 'o', 'c', 'k'};
+
+template <ByteLike BodyType>
+class MockCperEncoder : public CperEncoder<BodyType>
+{
+ public:
+ const uint64_t timestamp;
+
+ MockCperEncoder() :
+ timestamp(createCperTimestamp(std::chrono::system_clock::now()))
+ {}
+
+ MOCK_METHOD((void), mockCreateRecordHeader, ());
+ MOCK_METHOD((void), mockCreateSectionDescriptor, ());
+
+ RecordHeader createRecordHeader(const uint32_t recordLength,
+ const uint16_t sectionCount) override
+ {
+ mockCreateRecordHeader();
+ return RecordHeader(sectionCount, kMockRecordSeverity,
+ kMockValidationBits, recordLength, timestamp,
+ kMockPlatformId, kMockPartitionId, kMockCreatorId,
+ kMockNotificationType, kMockRecordId,
+ kMockHeaderFlags, kMockPersistenceInformation);
+ }
+
+ SectionDescriptor createSectionDescriptor(const uint32_t sectionLength,
+ const uint32_t sectionOffset,
+ const uint32_t sectionFlags,
+ const guid_t sectionType) override
+ {
+ mockCreateSectionDescriptor();
+ return SectionDescriptor(
+ sectionOffset, sectionLength, kMockSectionRevision,
+ kMockSectionValidationBits, sectionFlags, sectionType, kMockFruId,
+ kMockSectionSeverity, kMockFruText);
+ }
+};
+
+}; // namespace uefi::cper