blob: 498c8761c42e5251f731367492900d7b2191b30b [file] [edit]
#pragma once
#include "cper.hpp"
#include <absl/base/internal/endian.h>
#include <span>
#include <stdexcept>
#include <vector>
namespace uefi::cper
{
template <typename T>
inline T littleEndianLoad(const void* p)
{
if constexpr (sizeof(T) == 1)
return *static_cast<const uint8_t*>(p);
if constexpr (sizeof(T) == 2)
return absl::little_endian::Load16(p);
if constexpr (sizeof(T) == 4)
return absl::little_endian::Load32(p);
if constexpr (sizeof(T) == 8)
return absl::little_endian::Load64(p);
}
class CperDecoder
{
public:
/**
* @brief Construct a new CperDecoder object
*
* @param[in] data - The CPER record data to decode.
*
* @throws std::invalid_argument If the data is too small to contain a
* RecordHeader or if the CPER signature is missing.
*/
explicit CperDecoder(std::span<const uint8_t> data) : data_(data)
{
if (data_.size() < kRecordHeaderSizeBytes)
{
throw std::invalid_argument("Data too small for RecordHeader");
}
const uint8_t* headerPtr = data_.data();
// Manual copy for the signature array since Load helpers usually target
// integers
std::array<uint8_t, kRecordSignatureSize> signature;
std::memcpy(signature.data(),
headerPtr + offsetof(RecordHeader, signature),
kRecordSignatureSize);
if (memcmp(signature.data(), kRecordSignature.data(),
kRecordSignatureSize) != 0)
{
throw std::invalid_argument("Invalid CPER signature");
}
}
/**
* @brief Get the RecordHeader from the CPER record.
*
* @return The RecordHeader struct.
*/
RecordHeader getHeader() const
{
RecordHeader header;
memcpy(&header, data_.data(), kRecordHeaderSizeBytes);
return header;
}
struct Section
{
SectionDescriptor descriptor;
std::span<const uint8_t> body;
};
/**
* @brief Get the sections from the CPER record.
*
* @return A vector of Section structs containing the descriptor and body.
*
* @throws std::runtime_error If the record is malformed (e.g. section
* offset or length out of bounds).
*/
std::vector<Section> getSections() const
{
const uint8_t* headerPtr = data_.data();
const uint16_t sectionCount = littleEndianLoad<uint16_t>(
headerPtr + offsetof(RecordHeader, sectionCount));
std::vector<Section> sections;
sections.reserve(sectionCount);
for (uint16_t i = 0; i < sectionCount; ++i)
{
const uint64_t descriptorOffset =
kRecordHeaderSizeBytes + (i * kSectionDescriptorSizeBytes);
if (descriptorOffset + kSectionDescriptorSizeBytes > data_.size())
{
throw std::runtime_error("Section descriptor out of bounds");
}
const uint8_t* descriptorPtr = data_.data() + descriptorOffset;
SectionDescriptor descriptor;
memcpy(&descriptor, descriptorPtr, kSectionDescriptorSizeBytes);
const uint32_t bodyOffset = littleEndianLoad<uint32_t>(
descriptorPtr + offsetof(SectionDescriptor, sectionOffset));
const uint32_t bodyLength = littleEndianLoad<uint32_t>(
descriptorPtr + offsetof(SectionDescriptor, sectionLength));
if (bodyOffset + bodyLength > data_.size())
{
throw std::runtime_error("Section body out of bounds");
}
sections.push_back(
{descriptor, data_.subspan(bodyOffset, bodyLength)});
}
return sections;
}
private:
std::span<const uint8_t> data_;
};
} // namespace uefi::cper