blob: ac99abaffc0113386c1aa3407c93b4c21c2786fc [file]
#include "bios.hpp"
#include <cstdint> // IWYU pragma: keep
#include <filesystem> // NOLINT
#include <fstream>
#include <memory>
#include <optional>
#include <string>
#include <system_error> // NOLINT
#include <vector>
#include <gmock/gmock.h> // IWYU pragma: keep
#include <gtest/gtest.h> // IWYU pragma: keep
#include "absl/cleanup/cleanup.h"
#include "absl/functional/any_invocable.h" // IWYU pragma: keep
#include "absl/log/log.h"
#include "boost/system/error_code.hpp" // NOLINT
#include "http_request.hpp"
#include "logging.hpp"
#include "dbus_utility.hpp"
#include "snapshot_fixture.hpp"
#include <nlohmann/json.hpp>
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "test/g3/mock_managed_store.hpp"
#include "test/g3/mock_managed_store_test.hpp"
// IWYU pragma: no_include <gtest/gtest-message.h>
// IWYU pragma: no_include <gtest/gtest-test-part.h>
// IWYU pragma: no_include "gtest/gtest_pred_impl.h"
// IWYU pragma: no_include <gmock/gmock-matchers.h>
// IWYU pragma: no_include <gtest/gtest-matchers.h>
namespace redfish {
namespace {
using ::dbus::utility::DbusVariantType;
using ::managedStore::KeyType;
using ::managedStore::ManagedType;
using ::managedStore::ValueType;
TEST(ResourceIdFromNameString, ResourceIdFromNameString) {
EXPECT_EQ(resourceIdFromNameString("system10"), "10");
}
TEST_F(SnapshotFixture,
SingleHostPlatformHandleBiosServiceGetTestReturnsCorrectResponse) {
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
KeyType key(ManagedType::kManagedSubtreePaths, "/", 0,
{"xyz.openbmc_project.Inventory.Item.System"});
absl::StatusOr<std::shared_ptr<ValueType>> systemObjectsPath =
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->getMockObjectFromManagedStore(key);
ASSERT_TRUE(systemObjectsPath.ok());
std::optional<dbus::utility::MapperGetSubTreePathsResponse>
originalSystemObjectsPaths =
systemObjectsPath.value()->managedSubtreePaths;
ASSERT_TRUE(originalSystemObjectsPaths.has_value());
dbus::utility::MapperGetSubTreePathsResponse mockSingleHostSystem1 = {
"/xyz/openbmc_project/inventory/system1"};
ASSERT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(
mockSingleHostSystem1))
.ok());
absl::Cleanup mockManagedStoreResetter = [key, originalSystemObjectsPaths]() {
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(
originalSystemObjectsPaths.value()))
.ok());
};
std::string originalPath = biosSettingFilepathFromConfig;
biosSettingFilepathFromConfig = "/tmp/soc_firmware/host/vmtppr.cfg";
absl::Cleanup pathResetter = [originalPath]() {
biosSettingFilepathFromConfig = originalPath;
};
std::string systemName = "system1";
#else
std::string systemName = "system";
#endif
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, systemName);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["@odata.id"], "/redfish/v1/Systems/" + systemName + "/Bios");
EXPECT_EQ(json["@odata.type"], "#Bios.v1_1_0.Bios");
EXPECT_EQ(
json["Actions"]["#Bios.ResetBios"]["target"],
"/redfish/v1/Systems/" + systemName + "/Bios/Actions/Bios.ResetBios");
EXPECT_EQ(json["Description"], "BIOS Configuration Service");
EXPECT_EQ(json["Id"], "BIOS");
EXPECT_EQ(json["Links"]["ActiveSoftwareImage"]["@odata.id"],
"/redfish/v1/UpdateService/FirmwareInventory/bios_active");
EXPECT_TRUE(json["Links"]["SoftwareImages"].is_array());
EXPECT_EQ(json["Links"]["SoftwareImages"].size(), 1);
EXPECT_EQ(json["Links"]["SoftwareImages"][0]["@odata.id"],
"/redfish/v1/UpdateService/FirmwareInventory/bios_active");
EXPECT_EQ(json["Links"]["SoftwareImages@odata.count"], 1);
EXPECT_EQ(json["Name"], "BIOS Configuration");
EXPECT_EQ(json["Oem"]["Google"]["MemTestBlob"], "");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture,
SingleHostPlatformHandleBiosServiceGetTestReturnsNotFound) {
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system1");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(SnapshotFixture,
SingleHostPlatformHandleBiosServiceGetSystemSubTreePathsResponseError) {
// Looking for
// kManagedSubtreePaths|/|0|xyz.openbmc_project.Inventory.Item.System
KeyType key(ManagedType::kManagedSubtreePaths, "/", 0,
{"xyz.openbmc_project.Inventory.Item.System"});
absl::StatusOr<std::shared_ptr<ValueType>> systemObjectsPath =
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->getMockObjectFromManagedStore(key);
ASSERT_TRUE(systemObjectsPath.ok());
// TODO(b/416746677): Uncomment after the bug is fixed
// ASSERT_EQ(systemObjectsPath.value()->managedType,
// ManagedType::kManagedSubtreePaths);
std::optional<dbus::utility::MapperGetSubTreePathsResponse>
originalSystemObjectsPaths =
systemObjectsPath.value()->managedSubtreePaths;
ASSERT_TRUE(originalSystemObjectsPaths.has_value());
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateErrorValueType(
originalSystemObjectsPaths.value(),
boost::system::errc::make_error_code(
boost::system::errc::io_error)))
.ok());
absl::Cleanup mockManagedStoreResetter = [key, originalSystemObjectsPaths]() {
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(
originalSystemObjectsPaths.value()))
.ok());
};
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_, "system");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
class SingleHostBiosBlobPatchTest : public SnapshotFixture {
protected:
static void createEmptyBlobFile() {
std::filesystem::path filePath(actualBlobFilePath);
std::error_code ec;
std::filesystem::create_directories(filePath.parent_path(), ec);
if (ec) {
LOG(ERROR) << "Failed to create directory " << filePath.parent_path()
<< ": " << ec.message();
FAIL();
}
if (std::filesystem::exists(actualBlobFilePath)) {
// remove file if already exists
std::error_code ec;
std::filesystem::remove(actualBlobFilePath, ec);
if (ec) {
LOG(ERROR) << "BIOS setting blob file " << actualBlobFilePath
<< " exists, failed to remove: " << ec.message();
FAIL();
}
}
// create a empty file to start
std::ofstream out(actualBlobFilePath);
if (!out.good()) {
LOG(ERROR) << "Failed to create or open file " << actualBlobFilePath;
FAIL();
}
out.close();
}
static void SetUpTestSuite() {
SnapshotFixture::SetUpTestSuite();
biosSettingFilepathFromConfig = biosSettingFilePathTemplate;
}
static void TearDownTestSuite() {
SnapshotFixture::TearDownTestSuite();
std::error_code ec;
std::filesystem::remove(actualBlobFilePath, ec);
if (ec) {
LOG(WARNING) << "Failed to remove BIOS setting blob file "
<< actualBlobFilePath
<< " in TearDownTestSuite():" << ec.message();
}
}
static void SetupSystemDbusMock(const std::string& systemName) {
managedStore::KeyType systemKey(
managedStore::ManagedType::kManagedSubtreePaths, "/", 0,
{"xyz.openbmc_project.Inventory.Item.System"});
std::vector<std::string> mockSystems = {"/xyz/openbmc_project/inventory/" +
systemName};
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
systemKey, managedStore::MockManagedStoreTest::CreateValueType(
mockSystems))
.ok());
}
void TearDown() override {
std::error_code ec;
std::filesystem::remove(actualBlobFilePath, ec);
if (ec) {
LOG(WARNING) << "Failed to remove BIOS setting blob file "
<< actualBlobFilePath << " in TearDown():" << ec.message();
}
}
static std::string GetTestSystemName() {
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
return "system1";
#else
return "system";
#endif
}
static inline const std::string biosSettingFilePathTemplate =
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
"/tmp/soc_firmware/host/vmtppr.cfg";
#else
"/tmp/oem_bios_setting";
#endif
static inline const std::string actualBlobFilePath =
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
"/tmp/soc_firmware/host0/vmtppr.cfg";
#else
"/tmp/oem_bios_setting";
#endif
};
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsPeerNotAuthenticated) {
// no BIOS setting file if createEmptyBlobFile() is not called
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer_authenticated to false
req.peer_authenticated = false;
patchBiosSettings(app_, req, share_async_resp_, GetTestSystemName());
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::forbidden);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsRequestNoProperArgument) {
// no BIOS setting file if createEmptyBlobFile() is not called
crow::Request req = CreateRequest("{\"Whatever\": {\"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, GetTestSystemName());
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsToNonExistSystem) {
// no BIOS setting file if createEmptyBlobFile() is not called
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
SetupSystemDbusMock(GetTestSystemName());
#endif
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, "system2");
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::not_found);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsBlobFileNotExist) {
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
SetupSystemDbusMock(GetTestSystemName());
#endif
biosSettingFilepathFromConfig = "/non_exist/oem_bios_setting";
absl::Cleanup bioSettingFilePathResetter = []() {
biosSettingFilepathFromConfig = biosSettingFilePathTemplate;
};
// no BIOS setting file if createEmptyBlobFile() is not called
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, GetTestSystemName());
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsWithNullBlob) {
// no BIOS setting file if createEmptyBlobFile() is not called
handleBiosSettingPatch(share_async_resp_, biosSettingFilepathFromConfig,
/*multiHost=*/false, /*memTestBlob=*/std::nullopt,
/*totalEnabledCores=*/std::nullopt,
/*systemName=*/"system");
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::bad_request);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsWithInvalidBlob) {
// no BIOS setting file if createEmptyBlobFile() is not called
// ABCDE= is a invalid base64 encoded string
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
SetupSystemDbusMock(GetTestSystemName());
#endif
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"ABCDE=\"}}}");
// peer is authenticated
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, GetTestSystemName());
RunIoUntilDone();
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest,
PatchBiosSettingsGetSystemSubTreePathsResponseError) {
// prepare the BIOS setting file
createEmptyBlobFile();
// Looking for
// kManagedSubtreePaths|/|0|xyz.openbmc_project.Inventory.Item.System
KeyType key(ManagedType::kManagedSubtreePaths, "/", 0,
{"xyz.openbmc_project.Inventory.Item.System"});
absl::StatusOr<std::shared_ptr<ValueType>> systemObjectsPath =
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->getMockObjectFromManagedStore(key);
ASSERT_TRUE(systemObjectsPath.ok());
// TODO(b/416746677): Uncomment after the bug is fixed
// ASSERT_EQ(systemObjectsPath.value()->managedType,
// ManagedType::kManagedSubtreePaths);
std::optional<dbus::utility::MapperGetSubTreePathsResponse>
originalSystemObjectsPaths =
systemObjectsPath.value()->managedSubtreePaths;
ASSERT_TRUE(originalSystemObjectsPaths.has_value());
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateErrorValueType(
originalSystemObjectsPaths.value(),
boost::system::errc::make_error_code(
boost::system::errc::io_error)))
.ok());
absl::Cleanup mockManagedStoreResetter = [key, originalSystemObjectsPaths]() {
ASSERT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(
key, managedStore::MockManagedStoreTest::CreateValueType(
originalSystemObjectsPaths.value()))
.ok());
};
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// set authenticated to true
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, GetTestSystemName());
RunIoUntilDone();
// PATCH request use no_content as convention to indicate a success
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::internal_server_error);
}
TEST_F(SingleHostBiosBlobPatchTest, PatchBiosSettingsSuccessfully) {
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
SetupSystemDbusMock(GetTestSystemName());
#endif
crow::Logger::setLogLevel(crow::LogLevel::Error);
// prepare the BIOS setting file
createEmptyBlobFile();
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
EXPECT_CALL(
*dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore()),
PostDbusCallToIoContextThreadSafe(
testing::_,
testing::An<
absl::AnyInvocable<void(const boost::system::error_code&)>&&>(),
"xyz.openbmc_project.pldm", "/xyz/openbmc_project/pldm",
"xyz.openbmc_project.PLDM.control", "loadSingleFileAndSyncCache",
"/tmp/soc_firmware/host0/vmtppr.cfg",
testing::Matcher<uint32_t>(uint32_t{0})))
.Times(1)
.WillOnce(
managedStore::SimulateSuccessfulAsyncPostDbusCallThreadSafeAction::
SimulateSuccessfulAsyncPostDbusCall());
#endif
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_,
GetTestSystemName());
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["Oem"]["Google"]["MemTestBlob"], "");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
if (std::filesystem::exists(actualBlobFilePath)) {
LOG(INFO) << actualBlobFilePath << " exists in test body";
}
share_async_resp_->res.clear();
crow::Request req =
CreateRequest("{\"Oem\": {\"Google\": {\"MemTestBlob\" : \"AAEC\"}}}");
// set authenticated to true
req.peer_authenticated = true;
patchBiosSettings(app_, req, share_async_resp_, GetTestSystemName());
RunIoUntilDone();
// PATCH request use no_content as convention to indicate a success
EXPECT_EQ(share_async_resp_->res.result(),
boost::beast::http::status::no_content);
share_async_resp_->res.clear();
handleBiosServiceGet(app_, CreateRequest(), share_async_resp_,
GetTestSystemName());
RunIoUntilDone();
json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["Oem"]["Google"]["MemTestBlob"], "AAEC");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
} // namespace
} // namespace redfish