blob: b3fb6e3a09de55c0327a742391963f98473a8658 [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 "diagnostics/cros_healthd/fetchers/storage/nvme_device_adapter.h"
#include <algorithm>
#include <cstdint>
#include <string>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include "diagnostics/common/statusor.h"
#include "diagnostics/cros_healthd/utils/file_utils.h"
namespace diagnostics {
namespace {
namespace mojo_ipc = ::chromeos::cros_healthd::mojom;
constexpr char kVendorIdFile[] = "device/device/subsystem_vendor";
constexpr char kProductIdFile[] = "device/device/subsystem_device";
constexpr char kRevisionFile[] = "device/device/revision";
constexpr char kConfigFile[] = "device/device/config";
constexpr char kModelFile[] = "device/model";
constexpr char kFirmwareVersionFile[] = "device/firmware_rev";
constexpr size_t kU64Size = 8;
// Extract from PCI local bus spec 2.2 from December 18, 1998
// (page 191, figure 6-1)
struct pci_config_space {
uint16_t notrequired[4];
uint8_t revision;
char rest[0];
} __attribute__((packed));
// Convenience wrapper for error status.
Status ReadFailure(const base::FilePath& path) {
return Status(StatusCode::kUnavailable,
base::StringPrintf("Failed to read %s", path.value().c_str()));
}
} // namespace
NvmeDeviceAdapter::NvmeDeviceAdapter(const base::FilePath& dev_sys_path)
: dev_sys_path_(dev_sys_path) {}
std::string NvmeDeviceAdapter::GetDeviceName() const {
return dev_sys_path_.BaseName().value();
}
StatusOr<mojo_ipc::BlockDeviceVendor> NvmeDeviceAdapter::GetVendorId() const {
uint32_t value;
if (!ReadInteger(dev_sys_path_, kVendorIdFile, &base::HexStringToUInt,
&value)) {
return ReadFailure(dev_sys_path_.Append(kVendorIdFile));
}
mojo_ipc::BlockDeviceVendor result;
result.set_nvme_subsystem_vendor(value);
return result;
}
StatusOr<mojo_ipc::BlockDeviceProduct> NvmeDeviceAdapter::GetProductId() const {
uint64_t value;
if (!ReadInteger(dev_sys_path_, kProductIdFile, &base::HexStringToUInt64,
&value)) {
return ReadFailure(dev_sys_path_.Append(kProductIdFile));
}
mojo_ipc::BlockDeviceProduct result;
result.set_nvme_subsystem_device(value);
return result;
}
StatusOr<mojo_ipc::BlockDeviceRevision>
NvmeDeviceAdapter::GetRevisionOnPre410Kernel() const {
mojo_ipc::BlockDeviceRevision result;
std::vector<char> bytes;
bytes.resize(sizeof(pci_config_space));
int read = base::ReadFile(dev_sys_path_.Append(kConfigFile), bytes.data(),
bytes.size());
// Failed to read the file.
if (read < 0)
return ReadFailure(dev_sys_path_.Append(kConfigFile));
// File present, but the config space is truncated, assume revision == 0.
if (read < sizeof(pci_config_space)) {
result.set_nvme_pcie_rev(0);
return result;
}
pci_config_space* pci = reinterpret_cast<pci_config_space*>(bytes.data());
result.set_nvme_pcie_rev(pci->revision);
return result;
}
StatusOr<mojo_ipc::BlockDeviceRevision> NvmeDeviceAdapter::GetRevision() const {
uint32_t value;
// Try legacy method if the revision file is missing.
if (!base::PathExists(dev_sys_path_.Append(kRevisionFile)))
return GetRevisionOnPre410Kernel();
if (!ReadInteger(dev_sys_path_, kRevisionFile, &base::HexStringToUInt,
&value))
return ReadFailure(dev_sys_path_.Append(kRevisionFile));
mojo_ipc::BlockDeviceRevision result;
result.set_nvme_pcie_rev(value);
return result;
}
StatusOr<std::string> NvmeDeviceAdapter::GetModel() const {
std::string model;
if (!ReadAndTrimString(dev_sys_path_, kModelFile, &model))
return ReadFailure(dev_sys_path_.Append(kModelFile));
return model;
}
StatusOr<mojo_ipc::BlockDeviceFirmware> NvmeDeviceAdapter::GetFirmwareVersion()
const {
std::string str_value;
auto path = dev_sys_path_.Append(kFirmwareVersionFile);
if (!ReadFileToString(path, &str_value))
return ReadFailure(path);
char bytes[kU64Size] = {0};
memcpy(bytes, str_value.c_str(), std::min(str_value.length(), kU64Size));
uint64_t value = *reinterpret_cast<uint64_t*>(bytes);
mojo_ipc::BlockDeviceFirmware result;
result.set_nvme_firmware_rev(value);
return result;
}
} // namespace diagnostics