| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "google3/host_commands.h" |
| |
| #include "ec_util.hpp" |
| #include "host_command_mock.hpp" |
| #include "payload_update.hpp" |
| |
| #include <xyz/openbmc_project/Control/Hoth/error.hpp> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| // NOLINTNEXTLINE(google-build-using-namespace) |
| using namespace std::literals; |
| using sdbusplus::error::xyz::openbmc_project::control::hoth::ResponseFailure; |
| |
| using ::testing::_; |
| using ::testing::Return; |
| |
| namespace google |
| { |
| namespace hoth |
| { |
| namespace internal |
| { |
| namespace |
| { |
| |
| auto const goodResponseStr = "\x03\xfd\x00\x00\x00\x00\x00\x00"s; |
| |
| class EcUtilTest : public ::testing::Test |
| { |
| protected: |
| EcUtilTest() : ecUtil(&hostCmd) |
| {} |
| |
| // Host Command interface handle |
| internal::HostCommandMock hostCmd; |
| |
| // EcUtil interface handle |
| internal::EcUtilImpl ecUtil; |
| }; |
| |
| MATCHER_P(dummyMatches, req, "") |
| { |
| const auto *const arg_req = static_cast<const uint8_t *>(arg); |
| return *arg_req == req; |
| } |
| |
| class EcUtilStatisticTest : public EcUtilTest |
| {}; |
| |
| TEST_F(EcUtilStatisticTest, sendCommandReturnsBadResultFails) |
| { |
| std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end()); |
| // Change the RspHeader.result to something other than EC_RES_SUCCESS |
| rsp[2] = internal::EC_RES_ERROR; |
| |
| EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE + |
| EC_PRV_CMD_HOTH_GET_STATISTICS, |
| ecUtil.kVersionZero, nullptr, 0)) |
| .WillOnce(Return(rsp)); |
| |
| EXPECT_THROW(ecUtil.getHothStatistics(), ResponseFailure); |
| } |
| |
| TEST_F(EcUtilStatisticTest, sendCommandReturnsGoodResponseSuccess) |
| { |
| std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end()); |
| for (uint16_t i = 0; i < 256; i++) |
| { |
| rsp.push_back(0); |
| } |
| |
| EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE + |
| EC_PRV_CMD_HOTH_GET_STATISTICS, |
| ecUtil.kVersionZero, nullptr, 0)) |
| .WillOnce(Return(rsp)); |
| |
| EXPECT_NO_THROW(ecUtil.getHothStatistics()); |
| } |
| |
| class EcUtilPersistentPanicTest : public EcUtilTest |
| {}; |
| |
| struct panic_host_command_response |
| { |
| RspHeader hdr; |
| uint8_t body[HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE]; |
| }; |
| |
| // Template of a persistent panic response, with the correct data_len, checksum |
| // and panic magic. |
| const panic_host_command_response kPanicResponseTemplate = { |
| .hdr = |
| { |
| .struct_version = 3, |
| .checksum = 0xb9, |
| .result = 0, |
| .data_len = HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE, |
| .reserved = 0, |
| }, |
| .body = |
| { |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x50, 0x6e, 0x63, 0x21, |
| }, |
| }; |
| |
| TEST_F(EcUtilPersistentPanicTest, incorrectResponseSizeThrows) |
| { |
| std::vector<uint8_t> rsp_buf(sizeof(kPanicResponseTemplate), 0); |
| auto* rsp = reinterpret_cast<panic_host_command_response*>(rsp_buf.data()); |
| *rsp = kPanicResponseTemplate; |
| rsp->hdr.data_len -= 1; |
| rsp->hdr.checksum += 1; |
| rsp_buf.resize(sizeof(kPanicResponseTemplate) - 1); |
| EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE + |
| EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO, |
| ecUtil.kVersionZero, _, |
| sizeof(ec_request_persistent_panic_info))) |
| .Times(2) |
| .WillRepeatedly(Return(rsp_buf)); |
| EXPECT_THROW(ecUtil.checkHothPersistentPanicInfo(), ResponseFailure); |
| EXPECT_THROW(ecUtil.getHothPersistentPanicInfo(), ResponseFailure); |
| } |
| |
| TEST_F(EcUtilPersistentPanicTest, incorrectPanicMagicReturnsNullopt) |
| { |
| std::vector<uint8_t> rsp_buf(sizeof(kPanicResponseTemplate), 0); |
| auto* rsp = reinterpret_cast<panic_host_command_response*>(rsp_buf.data()); |
| *rsp = kPanicResponseTemplate; |
| rsp->body[143] -= 1; |
| rsp->hdr.checksum += 1; |
| EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE + |
| EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO, |
| ecUtil.kVersionZero, _, |
| sizeof(ec_request_persistent_panic_info))) |
| .Times(2) |
| .WillRepeatedly(Return(rsp_buf)); |
| EXPECT_FALSE(ecUtil.checkHothPersistentPanicInfo()); |
| EXPECT_FALSE(ecUtil.getHothPersistentPanicInfo()); |
| } |
| |
| TEST_F(EcUtilPersistentPanicTest, correctHostCommandReturnsFullPanicRecord) |
| { |
| std::vector<uint8_t> rsp_bufs[12]; |
| for (int i = 0; i < 12; ++i) |
| { |
| rsp_bufs[i].resize(sizeof(panic_host_command_response)); |
| auto* rsp = |
| reinterpret_cast<panic_host_command_response*>(rsp_bufs[i].data()); |
| *rsp = kPanicResponseTemplate; |
| // Tweak the response a little bit to make each chunk slightly |
| // different. |
| rsp->body[0] += i; |
| rsp->body[HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE - 1] -= i; |
| } |
| EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE + |
| EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO, |
| ecUtil.kVersionZero, _, |
| sizeof(ec_request_persistent_panic_info))) |
| .Times(13) |
| .WillOnce(Return(rsp_bufs[0])) |
| .WillOnce(Return(rsp_bufs[0])) |
| .WillOnce(Return(rsp_bufs[1])) |
| .WillOnce(Return(rsp_bufs[2])) |
| .WillOnce(Return(rsp_bufs[3])) |
| .WillOnce(Return(rsp_bufs[4])) |
| .WillOnce(Return(rsp_bufs[5])) |
| .WillOnce(Return(rsp_bufs[6])) |
| .WillOnce(Return(rsp_bufs[7])) |
| .WillOnce(Return(rsp_bufs[8])) |
| .WillOnce(Return(rsp_bufs[9])) |
| .WillOnce(Return(rsp_bufs[10])) |
| .WillOnce(Return(rsp_bufs[11])); |
| |
| EXPECT_TRUE(ecUtil.checkHothPersistentPanicInfo()); |
| |
| auto panic = ecUtil.getHothPersistentPanicInfo(); |
| std::span<uint8_t> panic_buf(reinterpret_cast<uint8_t*>(&panic.value()), |
| sizeof(panic.value())); |
| for (uint8_t i = 0; i < 12; ++i) |
| { |
| size_t chunk_start = |
| static_cast<uint32_t>(i) * HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE; |
| EXPECT_EQ(panic_buf[chunk_start], i); |
| EXPECT_EQ( |
| panic_buf[chunk_start + HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE - 1], |
| static_cast<uint8_t>(-i)); |
| } |
| } |
| |
| } // namespace |
| } // namespace internal |
| } // namespace hoth |
| } // namespace google |