blob: c775a4d7bcf327f0e992458fd5b05ce0a07c724e [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "storage_info/storage_capability_reporter.h"
#include <inttypes.h>
#include <string>
#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/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <brillo/blkdev_utils/storage_utils.h>
#include <brillo/process/process.h>
#include <metrics/metrics_library.h>
namespace {
std::vector<uint8_t> ReadExtcsd(const base::FilePath& rootdev) {
std::vector<uint8_t> extcsd;
std::string extcsd_str;
std::string rootdev_str = rootdev.value();
if (rootdev_str.empty()) {
LOG(ERROR) << "Malformed rootdev: " << rootdev;
return {};
}
char id = rootdev_str[rootdev_str.length() - 1];
base::FilePath debugfs(
base::StringPrintf("/sys/kernel/debug/mmc%c/mmc%c:0001/ext_csd", id, id));
if (!base::ReadFileToString(debugfs, &extcsd_str)) {
PLOG(ERROR) << "Can not read ext_csd";
return {};
}
base::TrimWhitespaceASCII(extcsd_str, base::TRIM_ALL, &extcsd_str);
if (!base::HexStringToBytes(extcsd_str, &extcsd)) {
LOG(ERROR) << "Can not convert hex string: " << extcsd_str;
return {};
}
return extcsd;
}
std::vector<uint8_t> ReadIdCtrl(const base::FilePath& rootdev) {
brillo::ProcessImpl proc;
proc.AddArg("/usr/sbin/nvme");
proc.AddArg("id-ctrl");
proc.AddArg("-b");
proc.AddArg(rootdev.value());
proc.RedirectOutputToMemory(false);
int status = proc.Run();
if (status != 0) {
LOG(ERROR) << "Failed to run nvme cli: "
<< proc.GetOutputString(STDERR_FILENO);
return {};
}
std::string result = proc.GetOutputString(STDOUT_FILENO);
return std::vector<uint8_t>(result.begin(), result.end());
}
std::vector<uint8_t> ReadIdNs(const base::FilePath& rootdev) {
brillo::ProcessImpl proc;
proc.AddArg("/usr/sbin/nvme");
proc.AddArg("id-ns");
proc.AddArg("-b");
proc.AddArg(rootdev.value());
proc.RedirectOutputToMemory(false);
int status = proc.Run();
if (status != 0) {
LOG(ERROR) << "Failed to run nvme cli: "
<< proc.GetOutputString(STDERR_FILENO);
return {};
}
std::string result = proc.GetOutputString(STDOUT_FILENO);
return std::vector<uint8_t>(result.begin(), result.end());
}
int GetBit(const std::vector<uint8_t>& extcsd, int byte, int bit) {
return (extcsd.at(byte) >> bit) & 1;
}
} // namespace
std::vector<StorageCapabilities> CollectEmmcCaps(
const std::vector<uint8_t>& extcsd) {
if (extcsd.empty()) {
return {};
}
return std::vector<StorageCapabilities>{
StorageCapabilities::STORAGE_PRESENT,
GetBit(extcsd, 231, 0) ? StorageCapabilities::MMC_SEC_ERASE_SUPPORTED
: StorageCapabilities::MMC_SEC_ERASE_NOT_SUPPORTED,
GetBit(extcsd, 231, 4) ? StorageCapabilities::MMC_TRIM_SUPPORTED
: StorageCapabilities::MMC_TRIM_NOT_SUPPORTED,
GetBit(extcsd, 231, 6) ? StorageCapabilities::MMC_SANITIZE_SUPPORTED
: StorageCapabilities::MMC_SANITIZE_NOT_SUPPORTED,
GetBit(extcsd, 181, 0) ? StorageCapabilities::MMC_ERASE_CONT_ONE
: StorageCapabilities::MMC_ERASE_CONT_ZERO};
}
std::vector<StorageCapabilities> CollectNvmeCaps(
const std::vector<uint8_t>& idctrl, const std::vector<uint8_t>& idns) {
if (idctrl.empty() || idns.empty()) {
return {};
}
std::vector<StorageCapabilities> caps{
StorageCapabilities::STORAGE_PRESENT,
GetBit(idctrl, 265, 0) ? StorageCapabilities::NVME_APST_SUPPORTED
: StorageCapabilities::NVME_APST_NOT_SUPPORTED,
GetBit(idns, 33, 3) ? StorageCapabilities::NVME_DEALOC_WZ_SUPPORTED
: StorageCapabilities::NVME_DEALOC_WZ_NOT_SUPPORTED};
int dealoc_byte = (GetBit(idns, 33, 2) << 2) + (GetBit(idns, 33, 1) << 1) +
GetBit(idns, 33, 0);
switch (dealoc_byte) {
case 0:
caps.push_back(StorageCapabilities::NVME_DEALOC_BYTE_NA);
break;
case 1:
caps.push_back(StorageCapabilities::NVME_DEALOC_BYTE_00);
break;
case 2:
caps.push_back(StorageCapabilities::NVME_DEALOC_BYTE_FF);
break;
default:
caps.push_back(StorageCapabilities::NVME_DEALOC_BYTE_INVAL);
}
return caps;
}
std::vector<StorageCapabilities> CollectUfsCaps(const base::FilePath& rootdev) {
if (rootdev.empty()) {
return {};
}
return {StorageCapabilities::STORAGE_PRESENT};
}
std::vector<StorageCapabilities> CollectUnknownDevCaps(
const base::FilePath& rootdev) {
LOG(INFO) << "No capabilities are collected for the device: " << rootdev;
return {};
}
std::vector<StorageCapabilities> CollectCaps(const base::FilePath& rootdev) {
switch (brillo::StorageUtils().GetStorageType(base::FilePath("/"), rootdev)) {
case brillo::StorageType::emmc:
return CollectEmmcCaps(ReadExtcsd(rootdev));
case brillo::StorageType::nvme:
return CollectNvmeCaps(ReadIdCtrl(rootdev), ReadIdNs(rootdev));
case brillo::StorageType::ufs:
return CollectUfsCaps(rootdev);
default:
return CollectUnknownDevCaps(rootdev);
}
}
bool ReportCaps(const std::vector<StorageCapabilities>& caps) {
if (caps.empty()) {
return false;
}
bool success = true;
MetricsLibrary lib;
for (auto c : caps) {
LOG(INFO) << "Sending capability to UMA: " << c;
success &= lib.SendSparseToUMA("Platform.StorageCapabilities", c);
}
return success;
}