blob: 44fbde0ef4b74170138162884a6f802cfe207fdd [file] [log] [blame]
// Copyright 2019 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/nvme_storage.h"
#include <pcrecpp.h>
#include <utility>
#include <base/files/file_util.h>
#include <base/json/json_reader.h>
#include <base/logging.h>
#include <brillo/errors/error.h>
#include <debugd/dbus-proxies.h>
#include "runtime_probe/system/context_instance.h"
#include "runtime_probe/utils/file_utils.h"
#include "runtime_probe/utils/value_utils.h"
namespace runtime_probe {
namespace {
constexpr auto kNvmeDevicePath = "device/device";
constexpr auto kNvmeDriverPath = "device/device/driver";
// Storage-speicific fields to probe for NVMe.
const std::vector<std::string> kNvmeFields{"vendor", "device", "class"};
constexpr auto kNvmeType = "NVMe";
constexpr auto kNvmePrefix = "pci_";
bool CheckStorageTypeMatch(const base::FilePath& node_path) {
VLOG(2) << "Checking if \"" << node_path.value() << "\" is NVMe.";
const auto nvme_driver_path = node_path.Append(kNvmeDriverPath);
base::FilePath driver_symlink_target;
if (!base::ReadSymbolicLink(nvme_driver_path, &driver_symlink_target)) {
VLOG(1) << "\"" << nvme_driver_path.value() << "\" is not a symbolic link";
VLOG(2) << "\"" << node_path.value() << "\" is not NVMe.";
return false;
}
pcrecpp::RE nvme_driver_re(R"(drivers/nvme)", pcrecpp::RE_Options());
if (!nvme_driver_re.PartialMatch(driver_symlink_target.value())) {
return false;
}
VLOG(2) << "\"" << node_path.value() << "\" is NVMe.";
return true;
}
bool NvmeCliList(std::string* output) {
brillo::ErrorPtr error;
if (ContextInstance::Get()->debugd_proxy()->Nvme(/*option=*/"list", output,
&error)) {
return true;
}
LOG(ERROR) << "Debugd::Nvme failed: " << error->GetMessage();
return false;
}
base::Optional<base::Value> GetStorageToolData() {
std::string output;
if (!NvmeCliList(&output))
return base::nullopt;
auto value = base::JSONReader::Read(output);
if (!value) {
LOG(ERROR) << "Debugd::Nvme failed to parse output as json:\n" << output;
return base::nullopt;
}
return value;
}
} // namespace
base::Optional<base::Value> NvmeStorageFunction::ProbeFromSysfs(
const base::FilePath& node_path) const {
VLOG(2) << "Processnig the node \"" << node_path.value() << "\"";
if (!CheckStorageTypeMatch(node_path))
return base::nullopt;
const auto nvme_path = node_path.Append(kNvmeDevicePath);
auto nvme_res = MapFilesToDict(nvme_path, kNvmeFields, {});
if (!nvme_res)
return base::nullopt;
PrependToDVKey(&*nvme_res, kNvmePrefix);
nvme_res->SetStringKey("type", kNvmeType);
return nvme_res;
}
base::Optional<base::Value> NvmeStorageFunction::ProbeFromStorageTool(
const base::FilePath& node_path) const {
auto nvme_data = GetStorageToolData();
if (!nvme_data)
return base::nullopt;
const auto* devices = nvme_data->FindListKey("Devices");
if (!devices) {
LOG(ERROR) << "Cannot find \"Devices\" in nvme output.";
return base::nullopt;
}
const auto& device_name = node_path.BaseName();
base::Value result(base::Value::Type::DICTIONARY);
for (const auto& device : devices->GetList()) {
const auto* path = device.FindStringKey("DevicePath");
if (!path || base::FilePath(*path).BaseName() != device_name)
continue;
const auto* firmware = device.FindStringKey("Firmware");
if (firmware) {
result.SetStringKey("storage_fw_version", *firmware);
}
const auto* model = device.FindStringKey("ModelNumber");
if (model) {
result.SetStringKey("nvme_model", *model);
}
break;
}
return result;
}
} // namespace runtime_probe