| // 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; |
| |
| // Refer to SMBIOS specification. |
| /* |
| https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.3.0.pdf |
| */ |
| struct DmiMemoryRaw { |
| // Header |
| uint8_t type; |
| uint8_t length; |
| uint16_t handle; |
| |
| // Memory attributes |
| uint8_t pad_1[8]; // skipped values |
| uint16_t size; // bit15: 0=MiB, 1=KiB |
| uint8_t pad_2[2]; // skipped values |
| uint8_t locator; // string |
| uint8_t pad_3[4]; // skipped values |
| uint16_t speed; // in MHz |
| uint8_t manufacturer; // string |
| uint8_t serial_number; // string |
| uint8_t asset_tag; // string |
| uint8_t part_number; // string |
| } __attribute__((packed)); |
| |
| struct DmiMemory { |
| uint16_t size; |
| uint16_t speed; |
| std::string locator; |
| std::string part_number; |
| }; |
| |
| 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; |
| } |
| |
| std::unique_ptr<DmiMemory> GetDmiMemoryFromBlobData( |
| 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 GetMemoryInfo() { |
| MemoryFunction::DataType results{}; |
| |
| 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 = GetDmiMemoryFromBlobData( |
| 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.push_back(std::move(info)); |
| } |
| |
| return results; |
| } |
| |
| } // namespace |
| |
| MemoryFunction::DataType MemoryFunction::EvalImpl() const { |
| return GetMemoryInfo(); |
| } |
| |
| } // namespace runtime_probe |