blob: dbaec800b09c4e086bfcd4b5f5d01e091f89f3d9 [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "debugd/src/firmware_dump_utils.h"
#include <memory>
#include <utility>
#include <base/containers/fixed_flat_map.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <brillo/dbus/dbus_method_response.h>
#include <brillo/errors/error_codes.h>
#include <chromeos/dbus/debugd/dbus-constants.h>
#include "debugd/src/path_utils.h"
namespace {
// A look-up table for string representation of base directory that the device
// driver-specific debugfs will mount on.
// The full path to the debugfs file is model-dependent and may not exist when
// the driver unloads. The full path will be fetched by |FindDebugfsPath|.
constexpr auto kDirectoryToSearchMap =
base::MakeFixedFlatMap<debugd::FirmwareDumpType, std::string_view>(
{{debugd::FirmwareDumpType::WIFI, "/sys/kernel/debug/iwlwifi"}});
// A look-up table for debugfs file paths of each operation.
constexpr auto kDumperFileMap =
base::MakeFixedFlatMap<debugd::FirmwareDumpOperation, std::string_view>(
{{debugd::FirmwareDumpOperation::GenerateFirmwareDump,
"iwlmvm/fw_dbg_collect"},
{debugd::FirmwareDumpOperation::ClearFirmwareDumpBuffer,
"iwlmvm/fw_dbg_clear"}});
} // namespace
namespace debugd {
std::optional<base::FilePath> FindDebugfsPath(
const FirmwareDumpType& fwdump_type,
const FirmwareDumpOperation& fwdump_operation) {
if (!kDirectoryToSearchMap.contains(fwdump_type)) {
LOG(ERROR)
<< "Failed to find the debugfs base directory for firmware dump type: "
<< static_cast<uint32_t>(fwdump_type);
return std::nullopt;
}
base::FilePath dumper_dir_to_search =
path_utils::GetFilePath(kDirectoryToSearchMap.find(fwdump_type)->second);
if (!base::PathExists(dumper_dir_to_search)) {
LOG(ERROR) << "Failed to find debugfs base directory: "
<< dumper_dir_to_search.value();
return std::nullopt;
}
base::FileEnumerator dir_enum(dumper_dir_to_search, /*recursive=*/false,
base::FileEnumerator::DIRECTORIES);
if (!kDumperFileMap.contains(fwdump_operation)) {
LOG(ERROR)
<< "Failed to find the debugfs file for firmware dump operation: "
<< static_cast<uint32_t>(fwdump_operation);
return std::nullopt;
}
base::FilePath dumper_file =
base::FilePath(kDumperFileMap.find(fwdump_operation)->second);
for (base::FilePath dir_name = dir_enum.Next(); !dir_name.empty();
dir_name = dir_enum.Next()) {
base::FilePath dumper_path = dir_name.Append(dumper_file);
if (base::PathExists(dumper_path)) {
return dumper_path;
}
}
LOG(ERROR) << "Failed to find dumper file " << dumper_file.value()
<< " under sub-directories of " << dumper_dir_to_search.value();
return std::nullopt;
}
bool WriteToDebugfs(const FirmwareDumpType& fwdump_type,
const FirmwareDumpOperation& fwdump_operation,
std::string_view content) {
const auto dumper_path = FindDebugfsPath(fwdump_type, fwdump_operation);
if (!dumper_path.has_value()) {
return false;
}
if (!base::WriteFile(dumper_path.value(), content)) {
LOG(ERROR) << "Failed to trigger firmware dump by writing " << content
<< " into " << dumper_path->value();
return false;
}
return true;
}
void GenerateFirmwareDumpHelper(
std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<bool>> response,
const FirmwareDumpType& fwdump_type) {
switch (fwdump_type) {
case FirmwareDumpType::WIFI:
if (!WriteToDebugfs(fwdump_type,
FirmwareDumpOperation::GenerateFirmwareDump, "1")) {
response->ReplyWithError(FROM_HERE, brillo::errors::dbus::kDomain,
DBUS_ERROR_FAILED,
"Failed to write to debugfs");
return;
}
break;
default:
response->ReplyWithError(
FROM_HERE, brillo::errors::dbus::kDomain, DBUS_ERROR_FAILED,
"Firmware dump operation is not supported for type: " +
std::to_string(static_cast<uint32_t>(fwdump_type)));
return;
}
// Response indicates success/failure of the debugfs call. So far we delegate
// to the driver and assume the low-level execution is successful.
response->Return(true);
}
void ClearFirmwareDumpBufferHelper(
std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<bool>> response,
const FirmwareDumpType& fwdump_type) {
switch (fwdump_type) {
case FirmwareDumpType::WIFI:
if (!WriteToDebugfs(fwdump_type,
FirmwareDumpOperation::ClearFirmwareDumpBuffer,
"1")) {
response->ReplyWithError(FROM_HERE, brillo::errors::dbus::kDomain,
DBUS_ERROR_FAILED,
"Failed to write to debugfs");
return;
}
break;
default:
response->ReplyWithError(
FROM_HERE, brillo::errors::dbus::kDomain, DBUS_ERROR_FAILED,
"Firmware dump operation is not supported for type: " +
std::to_string(static_cast<uint32_t>(fwdump_type)));
return;
}
// Response indicates success/failure of the debugfs call. So far we delegate
// to the driver and assume the low-level execution is successful.
response->Return(true);
}
} // namespace debugd