blob: 229fb3ff1c497a1020f9f6ec108bb02612afc369 [file] [log] [blame]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_NIC_VEEPROM_ACCESSOR_BASE_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_NIC_VEEPROM_ACCESSOR_BASE_H_
#include <endian.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <ios>
#include <numeric>
#include <optional>
#include <string>
#include <vector>
#include "google/protobuf/timestamp.pb.h"
#include "absl/base/thread_annotations.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "tlbmc/hal/nic_veeprom/interface.h"
#include "veeprom.pb.h"
#include "sensor.pb.h"
namespace milotic_tlbmc::nic_veeprom {
namespace internal {
// Maximum size of the VEEPROM file.
constexpr size_t kMaxVeepromSize = 4096;
// Magic number for VEEPROM header.
constexpr uint32_t kHeaderMagic = 0x424d4353;
// Validates checksum for a buffer, assuming checksum field is 0.
inline bool ValidateChecksum(const std::vector<uint8_t>& data) {
uint8_t sum = std::accumulate(data.begin(), data.end(), uint8_t{0});
return sum == 0;
}
// Reads VEEPROM data of type T from the given sysfs path.
template <typename T>
absl::StatusOr<std::vector<uint8_t>> ReadVeeprom(const std::string& path) {
std::ifstream ifs(path, std::ios::binary | std::ios::ate);
if (!ifs) {
return absl::UnavailableError(
absl::StrFormat("Failed to open VEEPROM file: %s", path));
}
std::streamsize size = ifs.tellg();
if (size < sizeof(T)) {
return absl::UnavailableError(absl::StrFormat(
"VEEPROM file size %d is smaller than expected %d", size, sizeof(T)));
}
ifs.seekg(kMaxVeepromSize - sizeof(T), std::ios::beg);
std::vector<uint8_t> buf(sizeof(T));
if (!ifs.read(reinterpret_cast<char*>(buf.data()), sizeof(T))) {
return absl::UnavailableError(
absl::StrFormat("Failed to read VEEPROM file: %s", path));
}
return buf;
}
template <typename T>
class AccessorBase : public Accessor {
public:
explicit AccessorBase(const std::string& eeprom_path, int version)
: eeprom_path_(eeprom_path), version_(version) {}
// Reads VEEPROM data, validates checksum, version and header magic,
// and updates the internal sensor cache.
void DoRefresh() override {
absl::MutexLock lock(mutex_);
sensor_values_ = std::nullopt;
auto buf = ReadVeeprom<T>(eeprom_path_);
if (!buf.ok()) {
LOG(ERROR) << buf.status();
return;
}
if (!ValidateChecksum(*buf)) {
LOG(ERROR) << "VEEPROM V" << version_ << " checksum validation failed";
return;
}
uint16_t version;
memcpy(&version, buf->data() + offsetof(T, version), sizeof(version));
version = be16toh(version);
if (version != version_) {
LOG(ERROR) << "VEEPROM V" << version_ << " version mismatch: " << version;
return;
}
uint32_t header_magic;
memcpy(&header_magic, buf->data() + offsetof(T, sensor_header_magic),
sizeof(header_magic));
header_magic = be32toh(header_magic);
if (header_magic != kHeaderMagic) {
LOG(ERROR) << "VEEPROM V" << version_
<< " header magic mismatch: " << header_magic;
return;
}
T data;
memcpy(&data, buf->data(), sizeof(data));
sensor_values_ = data;
last_refresh_timestamp_ = absl::Now();
}
protected:
const std::string eeprom_path_;
const int version_;
mutable absl::Mutex mutex_;
std::optional<T> sensor_values_ ABSL_GUARDED_BY(mutex_);
absl::Time last_refresh_timestamp_ ABSL_GUARDED_BY(mutex_);
};
} // namespace internal
} // namespace milotic_tlbmc::nic_veeprom
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_NIC_VEEPROM_ACCESSOR_BASE_H_