blob: abd69c692356067b14f497738c2c433fdb93c960 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "runtime_probe/functions/memory.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <base/values.h>
namespace runtime_probe {
namespace {
constexpr char kSysfsDmiPath[] = "/sys/firmware/dmi/entries";
constexpr auto kMemoryType = 17;
uint16_t MemorySize(uint16_t size) {
// bit 15: 0=MB, 1=KB
if (size & (1UL << 15)) {
size = (size ^ (1UL << 15)) >> 10;
}
return size;
}
// SmbiosString gets the string associated with the given SMBIOS raw data.
// If the arguments are valid, |id|-th string in the SMBIOS string table is
// returned; otherwise, nullptr is returned.
std::unique_ptr<std::string> SmbiosString(const std::vector<uint8_t>& blob,
uint8_t skip_bytes,
uint8_t id) {
auto output = std::make_unique<std::string>();
if (id == 0)
return output;
uint8_t count = 0;
auto data = reinterpret_cast<const char*>(blob.data());
for (size_t i = skip_bytes, start_i = i; i < blob.size(); ++i) {
if (data[i] == '\0') {
++count;
if (count == id) {
output->assign(data + start_i, i - start_i);
return output;
}
start_i = i + 1;
}
}
return nullptr;
}
base::Value GetMemoryInfo() {
base::Value results(base::Value::Type::LIST);
const base::FilePath dmi_dirname(kSysfsDmiPath);
for (int entry = 0;; ++entry) {
const base::FilePath dmi_basename(
base::StringPrintf("%d-%d", kMemoryType, entry));
auto dmi_path = dmi_dirname.Append(dmi_basename);
if (!base::DirectoryExists(dmi_path))
break;
base::Value info(base::Value::Type::DICTIONARY);
std::string raw_bytes;
if (!base::ReadFileToString(dmi_path.Append("raw"), &raw_bytes)) {
LOG(ERROR) << "Failed to read file in sysfs: " << dmi_path.value();
continue;
}
auto dmi_memory = DmiMemory::From(
std::vector<uint8_t>(raw_bytes.begin(), raw_bytes.end()));
if (!dmi_memory) {
LOG(ERROR) << "Failed to parse DMI raw data: " << dmi_path.value();
continue;
}
// The field "slot" denotes to the entry number instead of the physical slot
// number, which refers to mosys' output. To be compatible with current
// HWID, we still preserve this field.
info.SetIntKey("slot", entry);
info.SetStringKey("path", dmi_path.value());
info.SetIntKey("size", dmi_memory->size);
info.SetIntKey("speed", dmi_memory->speed);
info.SetStringKey("locator", dmi_memory->locator);
info.SetStringKey("part", dmi_memory->part_number);
results.Append(std::move(info));
}
return results;
}
} // namespace
std::unique_ptr<DmiMemory> DmiMemory::From(const std::vector<uint8_t>& blob) {
if (blob.size() < sizeof(DmiMemoryRaw))
return nullptr;
DmiMemoryRaw dmi_memory_raw;
std::copy(blob.begin(), blob.begin() + sizeof(DmiMemoryRaw),
reinterpret_cast<uint8_t*>(&dmi_memory_raw));
if (dmi_memory_raw.length < sizeof(DmiMemoryRaw))
return nullptr;
auto dmi_memory = std::make_unique<DmiMemory>();
dmi_memory->size = MemorySize(dmi_memory_raw.size);
dmi_memory->speed = dmi_memory_raw.speed;
auto ret = SmbiosString(blob, dmi_memory_raw.length, dmi_memory_raw.locator);
if (!ret)
return nullptr;
dmi_memory->locator = std::move(*ret);
ret = SmbiosString(blob, dmi_memory_raw.length, dmi_memory_raw.part_number);
if (!ret)
return nullptr;
dmi_memory->part_number = std::move(*ret);
return dmi_memory;
}
MemoryFunction::DataType MemoryFunction::Eval() const {
auto json_output = InvokeHelperToJSON();
if (!json_output) {
LOG(ERROR) << "Failed to invoke helper to retrieve memory results.";
return {};
}
if (!json_output->is_list()) {
LOG(ERROR) << "Failed to parse json output as list.";
return {};
}
return DataType(json_output->TakeList());
}
int MemoryFunction::EvalInHelper(std::string* output) const {
auto results = GetMemoryInfo();
if (!base::JSONWriter::Write(results, output)) {
LOG(ERROR) << "Failed to serialize memory probed result to json string.";
return -1;
}
return 0;
}
} // namespace runtime_probe