Use BCD format timestamps
Tested: Unit tests, ran on GLP machine and checked decoded base64
encoded cper log https://paste.googleplex.com/5503804505325568#l=18
Google-Bug-Id: b/377739641
Change-Id: Idfeba55cf6f21ca8066fcbc555d6e30323f63289
Signed-off-by: Aryk Ledet <arykledet@google.com>
diff --git a/include/cper.hpp b/include/cper.hpp
index 8f0bbd9..500cf6e 100644
--- a/include/cper.hpp
+++ b/include/cper.hpp
@@ -170,15 +170,14 @@
const guid_t notificationTypeIn, uint64_t recordIdIn,
const uint32_t flagsIn,
const uint64_t setPersistenceInformationIn) :
- signature(kRecordSignature),
- revision(kRecordRevision), signatureEnd(kRecordSignatureEnd),
- sectionCount(sectionCountIn), errorSeverity(errorSeverityIn),
- validationBits(validationBitsIn), recordLength(recordLengthIn),
- timestamp(timestampIn), platformId(platformIdIn),
- partitionId(partitionIdIn), creatorId(creatorIdIn),
- notificationType(notificationTypeIn), recordId(recordIdIn),
- flags(flagsIn), persistenceInformation(setPersistenceInformationIn),
- reserved({0})
+ signature(kRecordSignature), revision(kRecordRevision),
+ signatureEnd(kRecordSignatureEnd), sectionCount(sectionCountIn),
+ errorSeverity(errorSeverityIn), validationBits(validationBitsIn),
+ recordLength(recordLengthIn), timestamp(timestampIn),
+ platformId(platformIdIn), partitionId(partitionIdIn),
+ creatorId(creatorIdIn), notificationType(notificationTypeIn),
+ recordId(recordIdIn), flags(flagsIn),
+ persistenceInformation(setPersistenceInformationIn), reserved({0})
{}
};
@@ -238,10 +237,9 @@
const guid_t sectionTypeIn, const guid_t fruIdIn,
const uint32_t sectionSeverityIn,
const std::array<uint8_t, 20> fruTextIn) :
- sectionOffset(sectionOffsetIn),
- sectionLength(sectionLengthIn), revision(revisionIn),
- validationBits(validationBitsIn), reserved(0), flags(flagsIn),
- sectionType(sectionTypeIn), fruId(fruIdIn),
+ sectionOffset(sectionOffsetIn), sectionLength(sectionLengthIn),
+ revision(revisionIn), validationBits(validationBitsIn), reserved(0),
+ flags(flagsIn), sectionType(sectionTypeIn), fruId(fruIdIn),
sectionSeverity(sectionSeverityIn), fruText(fruTextIn)
{}
};
diff --git a/include/cper_encoder.hpp b/include/cper_encoder.hpp
index 70d21cb..4258cf1 100644
--- a/include/cper_encoder.hpp
+++ b/include/cper_encoder.hpp
@@ -14,6 +14,21 @@
concept ByteLike = std::is_trivially_copyable_v<T> && sizeof(T) == 1;
/**
+ * @brief Helper function to convert time values into BCD format.
+ *
+ * @example You may have minutes represented as base10 20 which is 0x14. BCD
+ * expects the values to be represented as 0x20.
+ *
+ * @param[in] val - The value to convert to BCD.
+ *
+ * @returns a byte with the BCD val.
+ */
+uint8_t convertToBcd(uint8_t val)
+{
+ return (0x10 * (val / 10)) + (val % 10);
+}
+
+/**
* @brief Helper function to create a UTC UEFI CPER timestamp from a
* std::chrono time point.
* https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html#record-header
@@ -47,9 +62,16 @@
const uint8_t year = calendarYear % kCentury;
const uint8_t century = calendarYear / kCentury;
- const Timestamp timestamp(utcTime.tm_sec, utcTime.tm_min, utcTime.tm_hour,
- isPreciseBit, utcTime.tm_mday, utcTime.tm_mon,
- year, century);
+ const Timestamp timestamp{
+ .seconds = convertToBcd(utcTime.tm_sec),
+ .minutes = convertToBcd(utcTime.tm_min),
+ .hours = convertToBcd(utcTime.tm_hour),
+ .isPrecise = isPreciseBit,
+ .day = convertToBcd(utcTime.tm_mday),
+ // tm_mon is months since January, add one to get current month.
+ .month = convertToBcd(utcTime.tm_mon + 1),
+ .year = convertToBcd(year),
+ .century = convertToBcd(century)};
uint64_t ret;
memcpy(&ret, ×tamp, sizeof(ret));
diff --git a/include/guid.hpp b/include/guid.hpp
index f734161..6bd692d 100644
--- a/include/guid.hpp
+++ b/include/guid.hpp
@@ -22,8 +22,8 @@
constexpr Guid(const uint32_t timeLowIn, const uint16_t timeMidIn,
const uint16_t timeHiAndVersionIn,
const std::array<uint8_t, 8> bytesIn) :
- timeLow(timeLowIn),
- timeMid(timeMidIn), timeHiAndVersion(timeHiAndVersionIn), bytes(bytesIn)
+ timeLow(timeLowIn), timeMid(timeMidIn),
+ timeHiAndVersion(timeHiAndVersionIn), bytes(bytesIn)
{}
constexpr bool operator==(const Guid& rhs) const
diff --git a/meson.build b/meson.build
index 0e06631..1003aba 100644
--- a/meson.build
+++ b/meson.build
@@ -27,6 +27,6 @@
description: 'Utilities for easily creating CPER logs'
)
-if get_option('tests').enabled()
+if get_option('tests').allowed()
subdir('test')
endif
diff --git a/meson.options b/meson.options
index b6cb722..92f99ae 100644
--- a/meson.options
+++ b/meson.options
@@ -1 +1 @@
-option('tests', type: 'feature', value: 'auto', description: 'Build tests')
+option('tests', type: 'feature', value: 'disabled', description: 'Build tests')
diff --git a/test/cper_encoder_test.cpp b/test/cper_encoder_test.cpp
index 0f3a74e..78e6000 100644
--- a/test/cper_encoder_test.cpp
+++ b/test/cper_encoder_test.cpp
@@ -16,6 +16,17 @@
concept StringOrVector =
std::is_same_v<T, std::string> || std::is_same_v<T, std::vector<uint8_t>>;
+uint8_t bcdToDecimal(const uint8_t bcd)
+{
+ uint8_t tens = bcd >> 4;
+ uint8_t ones = bcd & 0x0F;
+ if (ones > 9 || tens > 9)
+ {
+ return 0;
+ }
+ return (tens * 10) + ones;
+}
+
class CperEncoderTest : public testing::Test
{
public:
@@ -111,6 +122,14 @@
Timestamp structTimestamp;
memcpy(&structTimestamp, &cperTimestamp, sizeof(structTimestamp));
+ structTimestamp.seconds = bcdToDecimal(structTimestamp.seconds);
+ structTimestamp.minutes = bcdToDecimal(structTimestamp.minutes);
+ structTimestamp.hours = bcdToDecimal(structTimestamp.hours);
+ structTimestamp.day = bcdToDecimal(structTimestamp.day);
+ structTimestamp.month = bcdToDecimal(structTimestamp.month) - 1;
+ structTimestamp.year = bcdToDecimal(structTimestamp.year);
+ structTimestamp.century = bcdToDecimal(structTimestamp.century);
+
time_t tt = std::chrono::system_clock::to_time_t(ref);
tm utcTime = *gmtime(&tt);